4

I have a flexbox container with 3 sections. The idea is that each section will be like an accordion and expand, but with any one of them expanded, I need the sections content to overflow vertically only if it would push the whole container past 100% height of the parent, while keeping within 100% of the height of the flexbox's container's container.

Here's a snippet of what I've tried with percentage heights.

html, body {
  height: 100%;
  margin: 0;
}

#container {
  width: 100%;
  height: 100%;
  background-color: gray;
  display: flex;
  flex-direction: column;
}

#a {
  flex: 0 1 auto;
}

#b {
  flex: 2 1 auto;
}

#c {
  flex: 0 1 auto;
}

#s2 {
  max-height: 100%;
  overflow: auto;
  background-color: lightblue;
}
<div id="container">
    <div id="a"><div>Section 1</div></div>
    <div id="b">
      <div>Section 2</div>
      <div id="s2">
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
      </div>
    </div>
    <div id="c"><div>Section 3</div></div>
</div>

What I'd like is to be able to get something like the following snippet, but without specifying explicit heights of the parent element or flex items.

html, body {
  height: 100%;
  margin: 0;
}

#container {
  width: 100%;
  height: 100%;
  background-color: gray;
  display: flex;
  flex-direction: column;
}

#a {
  flex: 0 1 auto;
}

#b {
  flex: 2 1 auto;
}

#c {
  flex: 0 1 auto;
}

#s2 {
  max-height: 300px;
  overflow: auto;
  background-color: lightblue;
}
<div id="container">
    <div id="a"><div>Section 1</div></div>
    <div id="b">
      <div>Section 2</div>
      <div id="s2">
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
      </div>
    </div>
    <div id="c"><div>Section 3</div></div>
</div>

Note that Section 3 takes up the slack remaining from Section 2's inferred flex-basis. I want Section 2 to grow to 100% and then overflow. Is this even possible with flexbox?

I was overwhelmed with similar-sounded questions, the closest of which I believe are:

Make a div fill the height of the remaining screen space - This is very similar but my question adds the "needs to scroll if it overflows 100%".

Child with max-height: 100% overflows parent - I think this is the root of the problem. Without having a set parent height, it cannot calculate the height to overflow from.

How to make a flexbox container to scroll? - Again a flex-basis of pixel units is needed. :(.

I'm able to using display: tables or other solutions. After typing this out I'm thinking that a javascript solution will be needed.

kukkuz
  • 41,512
  • 6
  • 59
  • 95
Nick
  • 4,901
  • 40
  • 61

3 Answers3

2

Check this https://codepen.io/anon/pen/EJYwmq

The problem is that items inside flexboxes have an implicit "min-height" equals to it's real height. You can set it's min-height: 0px.

html, body {
  height: 100%;
  margin: 0;
}

#container {
  height: 100%;
  background-color: gray;
  display: flex;
  flex-direction: column;
}

#b {
  flex: 100% 1 1;
  min-height: 0px; // this is important!
  overflow: hidden;
  display: flex; // make it a flex too so you have better control of the scrollbar
  flex-direction: column;
}

#s2 {
  min-height: 0px;
  max-height: 100%;
  overflow: auto;
  background-color: lightblue;
}
arieljuod
  • 15,460
  • 2
  • 25
  • 36
1

You are almost good. Simply make the #b element to be a flexbox container too and with overflow:hidden or min-height:0

html, body {
  height: 100%;
  margin: 0;
}

#container {
  width: 100%;
  height: 100%;
  background-color: gray;
  display: flex;
  flex-direction: column;
}

#a {
  flex: 0 1 auto;
}

#b {
  flex: 2 1 auto;
  overflow:hidden;
  /*added this*/
  display:flex;
  flex-direction:column;
  /**/
}

#c {
  flex: 0 1 auto;
}

#s2 {
  overflow: auto;
  background-color: lightblue;
}
<div id="container">
    <div id="a"><div>Section 1</div></div>
    <div id="b">
      <div>Section 2</div>
      <div id="s2">
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
      </div>
    </div>
    <div id="c"><div>Section 3</div></div>
</div>

Instead of making the #b a flexbox container you can also consider height:100% on s2

html, body {
  height: 100%;
  margin: 0;
}

#container {
  width: 100%;
  height: 100%;
  background-color: gray;
  display: flex;
  flex-direction: column;
}

#a {
  flex: 0 1 auto;
}

#b {
  flex: 2 1 auto;
  overflow:hidden;
}

#c {
  flex: 0 1 auto;
}

#s2 {
  height:100%;
  overflow: auto;
  background-color: lightblue;
}
<div id="container">
    <div id="a"><div>Section 1</div></div>
    <div id="b">
      <div>Section 2</div>
      <div id="s2">
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
      </div>
    </div>
    <div id="c"><div>Section 3</div></div>
</div>
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
1

The #b section of your accordion fills the remaining space in the #container - so what you can do is split this vertical space among its contents - the heading and the #s2:

  • make #b a flexbox with flex-direction: column
  • also remember to set min-height: 0 for #b (min-height is auto by default for a flex item)
  • flex: 1 would be enough in #b instead of flex: 2 1 auto
  • also add flex: 1 to #s2 so that it fills the remaining space inside #b (you can drop the max-height: 100%).

See demo below:

html, body {
  height: 100%;
  margin: 0;
}

#container {
  width: 100%;
  height: 100%; 
  background-color: gray;
  display: flex;
  flex-direction: column;
}

#a {
  flex: 0 1 auto;
}

#b {
  flex: 1; /* this would do */
  display: flex; /* added*/
  flex-direction: column; /* added */
  min-height: 0; /* added */
}

#c {
  flex: 0 1 auto;
}

#s2 {
  flex: 1; /* added */
  /* max-height: 100%;*/
  overflow: auto;
  background-color: lightblue;
}
<div id="container">
    <div id="a"><div>Section 1</div></div>
    <div id="b">
      <div>Section 2</div>
      <div id="s2">
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
      </div>
    </div>
    <div id="c"><div>Section 3</div></div>
</div>

Now you have full-viewport accordion - you can also let the #container to take only as much space as it needs when an accordion item is expanded and allow it to expand till the viewport height and overflow only afterwards - just change height: 100% in container to max-height: 100% - see demo below:

html, body {
  height: 100%;
  margin: 0;
}

#container {
  width: 100%;
  max-height: 100%; /* changed to max-height */
  background-color: gray;
  display: flex;
  flex-direction: column;
}

#a {
  flex: 0 1 auto;
}

#b {
  flex: 1; /* this would do */
  display: flex; /* added*/
  flex-direction: column; /* added */
  min-height: 0; /* added */
}

#c {
  flex: 0 1 auto;
}

#s2 {
  flex: 1; /* added */
  /* max-height: 100%;*/
  overflow: auto;
  background-color: lightblue;
}
<div id="container">
    <div id="a"><div>Section 1</div></div>
    <div id="b">
      <div>Section 2</div>
      <div id="s2">
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
       <div>Item</div>
      </div>
    </div>
    <div id="c"><div>Section 3</div></div>
</div>
kukkuz
  • 41,512
  • 6
  • 59
  • 95