Egor
Egor

Reputation: 433

Flexbox layout: shrink and grow at the same time

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>

Upvotes: 2

Views: 628

Answers (2)

Asons
Asons

Reputation: 87191

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>

Upvotes: 2

Michael Benjamin
Michael Benjamin

Reputation: 370993

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

Upvotes: 4

Related Questions