2

I am trying to achieve a layout that stretches to fill the screen, but then scrolls when the content is larger than the available pane.

I have a basic flexbox approach based on this answer, and it does a good job filling up the screen, but overflowing content causes the whole screen to scroll rather than just its own container.

If I change .outer height to a literal value like 200px then I get the scrolling behavior that I want, but the bottom content pane no longer fills up the screen.

I have tried using display: table and related CSS rather than flexbox, but ended up with the same result.

I have also considered using calc on the height of the content pane - something like

height: calc(100% - 60px);

but I want the header to be able to grow with its content, so I don't have a hard value for its height for the calculation.

I am looking for a pure CSS solution here rather than window sizing with Javascript of some flavor.

html,
body {
  height: 100%;
  margin: 0
}
.box {
  display: flex;
  flex-flow: column;
  height: 100%;
}
.box .row {
  border: 1px dotted grey;
  flex: 1 1 auto;
}
.box .row.header {
  background: aliceblue;
}
.box .row.content {
  background: pink;
}
.box .row.content .title {
  height: 40px;
  background: yellow;
}
.outer {
  height: -webkit-calc(100% - 40px);
  height: -moz-calc(100% - 40px);
  height: -o-calc(100% - 40px);
  height: calc(100% - 40px);
  overflow-y: auto;
}
.inner {
  height: 100%;
}
.text {
  height: 100px;
  margin-top: 10px;
  border: 1px solid gold;
}
<div class="box">
  <div class="row header">
    <p><b>header</b>
      <br />
      <br />(sized to content)</p>
  </div>
  <div class="row content">
    <div class="title">
      <b>content</b>
      (fills remaining space)
    </div>
    <div class="outer">
      <div class="inner">
        <div class="text">Bacon ipsum dolor amet tongue corned beef landjaeger sausage beef meatball, kielbasa pastrami turkey boudin hamburger ham hock chuck tail pork. Shankle tail cupim ribeye.</div>
        <div class="text">Bacon ipsum dolor amet tongue corned beef landjaeger sausage beef meatball, kielbasa pastrami turkey boudin hamburger ham hock chuck tail pork. Shankle tail cupim ribeye.</div>
        <div class="text">Bacon ipsum dolor amet tongue corned beef landjaeger sausage beef meatball, kielbasa pastrami turkey boudin hamburger ham hock chuck tail pork. Shankle tail cupim ribeye.</div>
        <div class="text">Bacon ipsum dolor amet tongue corned beef landjaeger sausage beef meatball, kielbasa pastrami turkey boudin hamburger ham hock chuck tail pork. Shankle tail cupim ribeye.</div>
        <div class="text">Bacon ipsum dolor amet tongue corned beef landjaeger sausage beef meatball, kielbasa pastrami turkey boudin hamburger ham hock chuck tail pork. Shankle tail cupim ribeye.</div>
        <div class="text">Bacon ipsum dolor amet tongue corned beef landjaeger sausage beef meatball, kielbasa pastrami turkey boudin hamburger ham hock chuck tail pork. Shankle tail cupim ribeye.</div>
        <div class="text">Bacon ipsum dolor amet tongue corned beef landjaeger sausage beef meatball, kielbasa pastrami turkey boudin hamburger ham hock chuck tail pork. Shankle tail cupim ribeye.</div>
      </div><!-- inner -->
    </div><!-- outer -->
  </div>
</div>
Community
  • 1
  • 1
Danny
  • 1,740
  • 3
  • 22
  • 32

2 Answers2

3

Hope this is the behaviour you expected.

html,
body {
  height: 100%;
  widht: 100%;
}

body {
  margin: 0;
  padding: 0;
}

.box {
  display: flex;
  flex-flow: column;
  flex-wrap: nowrap;
  height: 100%;
}
.box .row {
  border: 1px dotted grey;
  flex: 0 0 auto;
  overflow: auto;
}
.box .row.header {
  background: aliceblue;
}
.box .row.content {
  flex: 1 1 auto;
  display: flex;
  flex-flow: column;
  flex-wrap: nowrap;
  background: pink;
}
.box .row.content .title {
  height: 40px;
  background: yellow;
  flex: 0 0 auto;
}
.text {
  height: 100px;
  margin-top: 10px;
  border: 1px solid gold;
}
.outer {
  height: 100%;
  flex: 1 1 auto;
  overflow-y: auto;
}
.inner {
  height: 100%;
}
<div class="box">
  <div class="row header">
    <p><b>header</b>
      <br />
      <br />(sized to content)</p>
  </div>
  <div class="row content">
    <div class="title">
      <b>content</b>
      (fills remaining space)
    </div>

    <div class="outer">

      <div class="inner">
        <div class="text">Bacon ipsum dolor amet tongue corned beef landjaeger sausage beef meatball, kielbasa pastrami turkey boudin hamburger ham hock chuck tail pork. Shankle tail cupim ribeye.</div>
        <div class="text">Bacon ipsum dolor amet tongue corned beef landjaeger sausage beef meatball, kielbasa pastrami turkey boudin hamburger ham hock chuck tail pork. Shankle tail cupim ribeye.</div>
        <div class="text">Bacon ipsum dolor amet tongue corned beef landjaeger sausage beef meatball, kielbasa pastrami turkey boudin hamburger ham hock chuck tail pork. Shankle tail cupim ribeye.</div>
        <div class="text">Bacon ipsum dolor amet tongue corned beef landjaeger sausage beef meatball, kielbasa pastrami turkey boudin hamburger ham hock chuck tail pork. Shankle tail cupim ribeye.</div>
        <div class="text">Bacon ipsum dolor amet tongue corned beef landjaeger sausage beef meatball, kielbasa pastrami turkey boudin hamburger ham hock chuck tail pork. Shankle tail cupim ribeye.</div>
        <div class="text">Bacon ipsum dolor amet tongue corned beef landjaeger sausage beef meatball, kielbasa pastrami turkey boudin hamburger ham hock chuck tail pork. Shankle tail cupim ribeye.</div>
        <div class="text">Bacon ipsum dolor amet tongue corned beef landjaeger sausage beef meatball, kielbasa pastrami turkey boudin hamburger ham hock chuck tail pork. Shankle tail cupim ribeye.</div>

      </div>
      <!-- inner -->

    </div>
    <!-- outer -->


  </div>
</div>
Jan Franta
  • 1,691
  • 2
  • 16
  • 25
  • Hmmm...closer, but I am looking for just the pink part to be scrollable. Also, could you point out specifically what change you made? – Danny May 04 '16 at 16:05
  • Ok updated the snippet at my answer. Please test it. – Jan Franta May 04 '16 at 16:59
  • Looks like the new version does the trick. Boiled down, it looks like a flex-within-a-flex? – Danny May 04 '16 at 18:12
  • Yes. Flex nesting is usual. – Jan Franta May 04 '16 at 18:35
  • I've added the nesting flexes to my real code and it looks to have fixed it. If you add some sort of explanation about that being necessary to your answer then I'll go ahead and accept it. As is, it's not immediately obvious what you changed to fix it, and I even wrote the original stylesheet. :) – Danny May 04 '16 at 19:11
1

The key to this layout is recognizing a flex item's minimum size:

4.5. Implied Minimum Size of Flex Items

To provide a more reasonable default minimum size for flex items, this specification introduces a new auto value as the initial value of the min-width and min-height properties defined in CSS 2.1.

In other words, a flex item will, by default, never be smaller than the size of its content.

Initial settings on a flex item:

min-width: auto;
min-height: auto;

This has the potential to force items to overflow their container.

The solution is to apply min-width: 0 for horizontal scroll, and min-height: 0 for vertical scroll, on the flex container.

Below is a simplified demonstration using your code (the JS is only to repeat the .text elements):

function multiplyNode(node, count, deep) {
    for (var i = 0, copy; i < count - 1; i++) {
        copy = node.cloneNode(deep);
        node.parentNode.insertBefore(copy, node);
    }
}

multiplyNode(document.querySelector('.text'), 10, true);
* { box-sizing: border-box; }

html, body {
  height: 100%;
  margin: 0;
  border: 1px solid black;
}

.box {
  display: flex;
  flex-flow: column;
  height: 100%;
}

.header {
    background-color: aqua;
    flex: 0 0 auto;
}

.content {
    background-color: yellow;
    flex: 1;
    display: flex;              /* new */
    flex-direction: column;     /* new */   
    min-height: 0;              /* new; allow flex item to be smaller than its content,
                                   enabling scroll */
}

.outer {
    background-color: pink;
    flex: 1;
    overflow-y: auto;
}

.text {
  height: 100px;
  margin-top: 10px;
  border: 1px solid gold;
}
<div class="box">
     <div class="header">header (sized to content, i.e., height: auto;)</div>
     <div class="content">content (fills remaining space, i.e. flex: 1;)
          <div class="outer">
              <div class="inner">
                   <div class="text">Bacon ipsum dolor amet tongue corned beef landjaeger sausage beef meatball, kielbasa pastrami turkey boudin hamburger ham hock chuck tail pork. Shankle tail cupim ribeye.</div>
                   </div><!-- inner -->
          </div><!-- outer -->
     </div><!-- content -->
</div><!-- box -->

Tested on Chrome, FF and IE11.

More information:

Community
  • 1
  • 1
Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
  • I get your point, but if I remove the `min-height` from your snippet the layout still works, even on auto. Conversely, if I add the min-height to *my* version where the sub-elements of .content are normal divs it doesn't fix it, until I make `.content` use its own flex layout. That seems to be the deciding factor in the height of the bottom pane. – Danny May 04 '16 at 18:31
  • Yes, I would suggest making `.content` a nested, column-direction flex container for this layout to work. – Michael Benjamin May 04 '16 at 18:33