Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Flexbox, have elements stretch to fill gap between rows [duplicate]

Tags:

html

css

flexbox

I feel a little silly asking this, but I've sort of exhausted my knowledge of Flexboxes, so I'm hoping maybe someone else can come in and help me out here.

My overall goal is to just have the two items in the middle row stretch to fill the space between the header and the items, and I have searched around and honestly can't figure out what it is that I should do. I forked the code from the CSS Tricks Guide, the one at the very bottom, and I've made some alterations. The code I currently have is (open it in full screen mode to make it more clear):

body,
html {
  height: 100%;
}
.wrapper {
  display: -webkit-box;
  display: -moz-box;
  display: -ms-flexbox;
  display: -webkit-flex;
  display: flex;
  justify-content: flex-start;
  -webkit-flex-flow: row wrap;
  flex-flow: row wrap;
  height: 100%;
  font-weight: bold;
  text-align: center;
}
.wrapper > * {
  padding: 10px;
  flex: 1 1 100%;
}
.header {
  background: tomato;
  height: 50px;
  flex: 1 1 100%;
}
.footer {
  background: lightgreen;
  height: 50px;
}
.main {
  text-align: left;
  align-self: stretch;
  background: deepskyblue;
}
.aside-1 {
  background: gold;
}
.aside-2 {
  background: hotpink;
}
@media all and (min-width: 600px) {
  .aside {
    flex: 1 auto;
  }
}
@media all and (min-width: 800px) {
  .main {
    flex: 3 0px;
  }
  .aside-1 {
    order: 1;
  }
  .main {
    order: 2;
  }
  .aside-2 {
    order: 3;
  }
  .footer {
    order: 4;
  }
}
body {
  padding: 2em;
}
<div class="wrapper">
  <header class="header">Header</header>
  <article class="main">
    <p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est.
      Mauris placerat eleifend leo.</p>
  </article>
  <aside class="aside aside-1">Aside 1</aside>

  <footer class="footer">Footer</footer>
</div>

Is it possible in flexbox to achieve this without changing the HTML, or should I just look for another way to accomplish this goal?

like image 815
Jhecht Avatar asked Jan 08 '17 01:01

Jhecht


People also ask

Does flexbox have gap?

CSS row-gap property:The row-gap CSS property for both flexbox and grid layouts allows you to create a gap between rows.

What are the disadvantages of using flexbox?

One of the major drawbacks of using flexbox is performance issues. The use of flexbox can increase the page loading time if you have a larger project. You can compare the page loading time through Chrome development tools using the flexbox and the same pages constructed without using flexbox.


2 Answers

The idea is to wrap them around a container and use flex-grow:1; on that container, this will make the container fill the space between the header and footer..

Then in the @media query, change the flex-direction of this container to row. This will make the .main and aside to come side by side on big screens.

body,
html {
  height: 100%;
}
.wrapper {
  display: -webkit-box;
  display: -moz-box;
  display: -ms-flexbox;
  display: -webkit-flex;
  display: flex;
  justify-content: flex-start;
  flex-direction:column;
  height: 100%;
  font-weight: bold;
  text-align: center;
}
.wrapper > * {
  padding: 10px;
}
.header {
  background: tomato;
  height: 50px;
  flex-shrink:0;
}
.footer {
  background: lightgreen;
  height: 50px;
  flex-shrink:0;
}
.main {
  text-align: left;
  //align-self: stretch;
  background: deepskyblue;
  padding:10px;
}
.main p{
  margin:0;
  padding:0;
}
.aside-1 {
  background: gold;
}
.aside-2 {
  background: hotpink;
}
.container{
  width:100%;
  margin:0;
  padding:0;
  flex-grow:1;
  flex-shrink:0;
}
@media all and (min-width: 600px) {
  .aside {
    flex: 1 auto;
  }
}
@media all and (min-width: 800px) {
  .container{
    display:flex;
    flex-direction:row;
  }
  .main {
    flex: 3 0px;
    flex-grow:1;
    flex-shrink:0;
  }
  .aside-1 {
    order: 1;
    flex-grow:1;
    flex-shrink:0;
  }
  .main {
    order: 2;
  }
  .aside-2 {
    order: 3;
  }
  .footer {
    order: 4;
  }
}
body {
  padding: 2em;
}
<div class="wrapper">
  <header class="header">Header</header>
  <div class="container">
  <article class="main">
    <p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est.
      Mauris placerat eleifend leo.</p>
  </article>
  <aside class="aside aside-1">Aside 1</aside>
    </div>
  <footer class="footer">Footer</footer>
</div>
like image 27
ab29007 Avatar answered Oct 07 '22 22:10

ab29007


While people are telling you how to resolve the issue, they are not telling you why you don't have the result you expected. I think it's partly because most of them missed the actual question. Which I found really interesting.

Let me get some things out of the way first :

Flex-direction:: For practical purpose it means the direction in which the items are displayed. However it's not accurate.

For now let's say that if the direction is set to row, it means that each item must have the height of the container and they should be put next to each other. In other words the container has to be considered a row and the item are the columns.

.c{
  display: flex;
  width: 400px;
  height:100px;
}
.c1{
  flex-grow: 1;
  background:gold;

}
.c2{
  flex-grow: 1;
  background:red;
}
<div class="c">
    <div class="c1"></div>
    <div class="c2"></div>
</div>

I didn't specify an height, the items filled the height of the row and stacked against each others like columns.


When you specify an height the item will take the height you defined but that does not change the height of the row :

.c{
  display: flex;
  width: 400px;
  height: 100px;
}
.c1{
  flex-grow: 1;
  height: 40px;
  background:gold;

}
.c2{
  flex-grow: 1;
  background:red;
}
<div class="c">
    <div class="c1"></div>
    <div class="c2"></div>
</div>

The red cube still spawn the vertical space because the height of the row hasn't changed.


flex grow: the amount of free space distributed to the different items.

.c{
    display: flex;
    width: 400px;
  }

.c1{
  flex-grow: 1;
  background:gold;
  }

.c2{
  flex-grow: 1;
  background:red;
  }
    <div class="c">
        <div class="c1">AAAAAAAAAAAAA</div>
        <div class="c2"></div>
    </div>

Despite having the same flex-grow value, those two items aren't the same size, that is because the free space is distributed among them but the yellow rectangle was bigger to begin with.


First let's use flex-wrap : wrap:

.c{
  display: flex;
  flex-wrap: wrap;
  border: 1px solid black;
  width: 400px;
  height:100px;
}
.c1{
  width:200px;
  background:gold;

}
.c2{
  width:200px;
  background:red;
}

.c3{
  width:100px;
  background:orange;

}
.c4{
  width:300px;
  background:green;
}
<div class="c">
    <div class="c1"></div>
    <div class="c2"></div>
    <div class="c3"></div>
    <div class="c4"></div>
</div>

As we can see when we go beyond the amount of width available the items start under, effectively creating another row.


Now to address your question:

What if we took the example above and set the height of the first item ? Let's see:

    .c{
      display: flex;
      flex-wrap: wrap;
      border: 1px solid black;
      width: 400px;
      height:100px;
    }
    .c1{
      width:200px;
      height: 30px;
      background:gold;

    }
    .c2{
      width:200px;
      background:red;
    }

    .c3{
      width:200px;
      background:orange;

    }
    .c4{
      width:200px;
      background:green;
    }
<div class="c">
  <div class="c1"></div>
  <div class="c2"></div>
  <div class="c3"></div>
  <div class="c4"></div>
</div>

Like in your snippet.

Let's see another example:

        .c{
          display: flex;
          flex-wrap: wrap;
          border: 1px solid black;
          width: 600px;
          height:100px;
        }
        .c1{
          width:400px;
          height: 35px;
          background:gold;

        }
        .c2{
          width:200px;
          background:red;
        }

        .c3{
          width:200px;
          background:orange;

        }
        .c4{
          width:200px;
          background:green;
        }
        .c5{
          width:200px;
          background:purple;
        }
    <div class="c">
      <div class="c1"></div>
      <div class="c2"></div>
      <div class="c3"></div>
      <div class="c4"></div>
      <div class="c5"></div>
    </div>
  1. yellow cube of 400px X 35px is put and spans 2 columns, then red cube of 200px is put and spans 1 column.

  2. At this point all the rectangles have 0 height except the first one which has 35px.

  3. The remaining vertical space is divided between the rows as to spawn the whole vertical space. Thus the remaining vertical space is 100-35 = 65px. divided in 2 rows = 32.5. The first row gets 35 + 32.5 and the second row gets 32.5px height.

Another example to make things clearer:

    .c, .d{
              display: flex;
              flex-wrap: wrap;
              border: 1px solid black;
              width: 600px;
              height:100px;
            }

            .c1{
              flex-shrink: 0;
              width:400px;
              height: 0px;
              background:gold;

            }
            .c2{
              width:200px;
              background:red;
            }

            .c3{
              width:200px;
              background:orange;

            }
            .c4{
              width:200px;
              background:green;
            }
            .c5{
              width:200px;
              background:purple;
            }


            .d1{
              width:400px;
              height: 50px;
              background:gold;

            }
            .d2{
              width:200px;
              background:red;
            }

            .d3{
              width:200px;
              background:orange;

            }
            .d4{
              width:200px;
              background:green;
            }
            .d5{
              width:200px;
              background:purple;
            }
First item has 0px height, the vertical space remaining (100px) is divided between the 2 rows. Both row have 50px height
        <div class="c">
          <div class="c1"></div>
          <div class="c2"></div>
          <div class="c3"></div>
          <div class="c4"></div>
          <div class="c5"></div>
        </div>

First item has 35px height, the vertical space remaining (65px) is divided between the 2 rows.
        <div class="d">
          <div class="d1"></div>
          <div class="d2"></div>
          <div class="d3"></div>
          <div class="d4"></div>
          <div class="d5"></div>
        </div>

To resolve this you can use calc() to calculate the other rows height like others suggested. The reason is that there is no more free vertical space to be shared.

            .c{
              display: flex;
              flex-wrap: wrap;
              border: 1px solid black;
              width: 600px;
              height:100px;
            }
            .c1{
              width:400px;
              height: 35px;
              background:gold;

            }
            .c2{
              width:200px;
              background:red;
            }

            .c3{
              height:calc(100% - 35px);
              width:600px;
              background:green;

            }
            
            
        <div class="c">
          <div class="c1"></div>
          <div class="c2"></div>
          <div class="c3"></div>
        </div>
like image 71
Ced Avatar answered Oct 07 '22 21:10

Ced