1

I am struggling to make a simple layout using flexbox.

Layout: two divs.

  1. On width less than 300px - they should stack on top of each other and occupy all available space.
  2. On width more than 300px - they should take 50% of width each.
  3. If one div doesn't contain anything - it should not occupy any space (basically it should be hidden) and remaining div should occupy all the space available.

I've tried this arrangement below but failed. In this example, second block is wrong, then each block should occupy 50% of "one row". I've tried some other variants - all doesn't seem to work.

.container {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-around;
}

.div1, .div2 {
  flex-grow: 1;
  flex-shrink: 1;
  flex-basis: 50%;
}

.div1 {
  background-color: red;
}

.div2 {
  background-color: green;
}
<div class="container" style="width: 250px;">
  <div class='div1'>
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
    in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
  </div>

  <div class='div2'>
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
    in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
  </div>
</div>

<hr/>
<div class="container" style="width: 1000px;">
  <div class='div1'>
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
    in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
  </div>

  <div class='div2'>
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
    in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
  </div>
</div>

<hr/>
<div class="container" style="width: 1000px;">
  <div class='div1'>
  </div>

  <div class='div2'>
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
    in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
  </div>
</div>
Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
Egor
  • 423
  • 1
  • 4
  • 13

2 Answers2

3

A great method for solving this problem is provided in this answer by @LGSon.

As the answer points out, the key is to give each item flex-grow: 1.

Here's why it works:

These are the requirements from the question:

  1. two flex items
  2. on small screens, they should stack into a column
  3. on large screens, they should line up in a row 50/50
  4. if one item has no content, it should consume no space, and the other should consume all space

flex: 1 1 50%

This is the attempted solution in the question:

.div1, .div2 {
   flex-grow: 1;
   flex-shrink: 1;
   flex-basis: 50%;
}

This code won't work because it doesn't meet Requirement #4. Flex items will share available space equally (flex-grow: 1), after they are each sized to 50% width (flex-basis: 50%).

Since there is no free space left over when two items are 50/50, flex-grow has no effect and items will always be 50/50, regardless of content.

Note that the flex-basis property is, per spec guidelines, rendered first, before any free space is distributed by flex-grow and flex-shrink.

.container {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-around;
}

.div1,
.div2 {
  flex-grow: 1;
  flex-shrink: 1;
  flex-basis: 50%;
}

.div1 {
  background-color: red;
}

.div2 {
  background-color: green;
}
<div class="container" style="width: 250px;">
  <div class='div1'>
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
    in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
  </div>
  <div class='div2'>
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
    in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
  </div>
</div>
<hr/>
<div class="container" style="width: 1000px;">
  <div class='div1'>
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
    in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
  </div>
  <div class='div2'>
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
    in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
  </div>
</div>
<hr/>
<div class="container" style="width: 1000px;">
  <div class='div1'>
  </div>
  <div class='div2'>
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
    in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
  </div>
</div>

flex: 1

Using flex: 1 wouldn't work either. This is equivalent to:

  • flex-grow: 1
  • flex-shrink: 1
  • flex-basis: 0

Because flex items are initially sized to flex-basis: 0, all container space is distributed equally. Again, this violates Requirement #4, as both items will be 50/50, regardless of content.

.container {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-around;
}

.div1,
.div2 {
  flex: 1;
}

.div1 {
  background-color: red;
}

.div2 {
  background-color: green;
}
<div class="container" style="width: 250px;">
  <div class='div1'>
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
    in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
  </div>
  <div class='div2'>
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
    in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
  </div>
</div>
<hr/>
<div class="container" style="width: 1000px;">
  <div class='div1'>
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
    in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
  </div>
  <div class='div2'>
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
    in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
  </div>
</div>
<hr/>
<div class="container" style="width: 1000px;">
  <div class='div1'>
  </div>
  <div class='div2'>
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
    in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
  </div>
</div>

flex: auto

This is the only solution that works. It breaks down to:

  • flex-grow: 1
  • flex-shrink: 1
  • flex-basis: auto

This basically says:

  • The initial size of each flex item is the size of the content.
  • Once that size is determined, flex-grow can enter the picture to allot remaining space.

Since the initial size of an item with no content is zero, this allows the second item to occupy all space on the line.

The answer I cited uses flex-grow: 1 instead of flex: auto. That has the same effect since flex-shrink: 1 and flex-basis: auto are default values.

.container {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-around;
}

.div1,
.div2 {
  flex: auto;
}

.div1 {
  background-color: red;
}

.div2 {
  background-color: green;
}
<div class="container" style="width: 250px;">
  <div class='div1'>
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
    in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
  </div>
  <div class='div2'>
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
    in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
  </div>
</div>
<hr/>
<div class="container" style="width: 1000px;">
  <div class='div1'>
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
    in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
  </div>
  <div class='div2'>
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
    in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
  </div>
</div>
<hr/>
<div class="container" style="width: 1000px;">
  <div class='div1'>
  </div>
  <div class='div2'>
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
    in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
  </div>
</div>

More information

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
  • 1
    Thanks! This is exactly what I was looking for. – Egor Jul 03 '17 at 06:14
  • Actually, for my specific case, it didn't work, probably because each div contained different content with different sizes, and each wanted to take full width, so they wrapped on any width. So, after trial and error, what worked for me: .div1 {/*nothing set at all, not even flex: auto*/}, div2 {flex: 1 1 50%;} – Egor Jul 03 '17 at 06:39
2

I simplified your CSS a little.

  1. Set the container to display: flex (and remove flex-wrap: wrap; or else they won't sit side-by-side on wider screens)

  2. Set flex-grow: 1, take equal space left based on each items content

  3. Add a media query that when triggered at the requested width, here I used 600px (2 times 300px), changes the flex-direction to column,which will make them stack vertical.

Stack snippet

.container {
  display: flex;
}

.div1, .div2 {
  flex-grow: 1;
}
.div1 {
  background-color: red;
}
.div2 {
  background-color: green;
}

@media (max-width: 600px) {
  .container {
    flex-direction: column;
  }
}
<div class="container">
  <div class='div1'>
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
  </div>

  <div class='div2'>
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
  </div>
</div>

<hr/>
<div class="container">
  <div class='div1'>
  </div>

  <div class='div2'>
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
  </div>
</div>

Updated

If there will be a smaller amount of content in either of the 2 flex items (div1/div2), and you still want them to be equal width, change flex-grow: 1 to flex-basis: 100%.

Doing so they now will always take 100% of the width, but since they can't wrap, the flex-shrink property will come into play, which defaults to 1, and does the same as flex-grow, though will instead shrink the items equally.

Since this will also make the empty div take the same width, we need to address that, which we easily can do with this rule, where an :empty div will be sized to 0%

.div1:empty, .div2:empty {
  flex-basis: 0%;
}

Stack snippet

.container {
  display: flex;
}

.div1, .div2 {
  flex-basis: 100%;
}
.div1:empty, .div2:empty {
  flex-basis: 0%;
}

.div1 {
  background-color: red;
}
.div2 {
  background-color: green;
}

@media (max-width: 400px) {
  .container {
    flex-direction: column;
  }
}
<div class="container">
  <div class='div1'>
    "Lorem ipsum dolor sit amet."
  </div>

  <div class='div2'>
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
  </div>
</div>

<hr/>
<div class="container">
  <div class='div1'></div>

  <div class='div2'>
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
  </div>
</div>

Updated based on a comment

Instead of using media query, one can use flex-wrap in combination with min-width, flex-grow and flex-basis.

And this work like this, where flex-basis: 50% is their start value, flex-grow: 1 make them take all available space after they break line, and min-width make sure they break line when their parent's width becomes smaller than 2 times the min-width value.

And for the empty div we now need display: none instead.

Fiddle demo

Stack snippet

.container {
  display: flex;
  flex-wrap: wrap;
}

.div1, .div2 {
  flex-grow: 1;
  flex-basis: 50%;
  min-width: 150px;  
}
.div1:empty, .div2:empty {
  display: none;
}

.div1 {
  background-color: red;
}
.div2 {
  background-color: green;
}
<div class="container">
  <div class='div1'>
    "Lorem ipsum dolor sit amet."
  </div>

  <div class='div2'>
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
  </div>
</div>

<hr/>
<div class="container">
  <div class='div1'></div>

  <div class='div2'>
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
  </div>
</div>
Asons
  • 84,923
  • 12
  • 110
  • 165
  • Thank you for answering! While this works well, it feels a little bit like a hack, using media queries, I prefer not no user them whenever possible, also (I admit that I have omitted it from initial requirement for clarity) this "container" div is actually is not a container, but it's nested deep in DOM hierarchy. – Egor Jul 03 '17 at 06:13
  • @Egor Media query is absolutely not a hack, but there might be another way. Did I understood you correct, that my sample does the job, you simply would like it without a media query? .... Also, for me, as my answer actually solve your question, I find it odd you chose to accept another, which clearly state they based their on mine and its purpose was to give a deeper explanation. Since that answer alone won't make it work, and if you now find mine more useful, consider upvote and accept it. – Asons Jul 03 '17 at 07:21
  • sorry for not making it clear, media query does not help (as the width of container does not equal the screen width, as container is smaller because it's nested deep in hierarchy, and it's impossible to know it's width beforehand). To make it clear, it works for provided simplified example, but for my specific case. But another answer almost works for my case, and is more brief, so i consider it more "general" one, so I have chosen it instead. Thank you anyway again, hope I explained it well now. – Egor Jul 03 '17 at 11:15
  • @Egor Added a third sample that does not use media query – Asons Jul 03 '17 at 14:58