Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I force a nested flexbox element to shrink and display a scrollbar when the viewport resizes?

Tags:

html

css

flexbox

I'm building a layout using flexbox, and it's fairly simple overall. There are three parts of the view: a navigation bar at the top, a sidebar on the left, and a content area that fills the remaining space. The navigation bar has a fixed height, and the sidebar has a fixed width.

Furthermore, the sidebar and content areas should scroll individually. If the content in the sidebar overflows, it should create a scrollbar specific to the sidebar. The same is true with the content view. Importantly, this means that the overall viewport must never scroll: it should remain static (only the elements should scroll).

Building this layout is very simple with flexbox:

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

body {
  margin: 0;
}

#root {
  display: flex;
  height: 100%;
}

#frame {
  display: flex;
  flex: 1;
  flex-direction: column;
}

#navigation-bar {
  background-color: #bab;
  display: flex;
  height: 70px;
  overflow: hidden;
}

#main {
  display: flex;
  flex: 1;
}

#left-bar {
  background-color: #aaa;
  overflow: auto;
  width: 250px;
}

#content {
  background-color: #ccc;
  flex: 1;
}
<div id="root">
  <div id="frame">
    <div id="navigation-bar">
      <h1>Website Name</h1>
    </div>
    <div id="main">
      <div id="left-bar">
        <h1>Some content</h1>
        <h1>Some content</h1>
        <h1>Some content</h1>
        <h1>Some content</h1>
        <h1>Some content</h1>
        <h1>Some content</h1>
        <h1>Some content</h1>
        <h1>Some content</h1>
        <h1>Some content</h1>
      </div>
      <div id="content">
      </div>
    </div>
  </div>
</div>

However, notice that the sidebar does not scroll individually. Instead, the whole viewport expands and scrolls. Interestingly, what I'm trying to achieve works properly without the nesting—if I remove the navigation bar, the sidebar scrolls independently.

How can I prevent the flexbox itself from stretching to contain its contents so that the element-specific scrollbar is displayed, not the viewport's scrollbar?

like image 528
Alexis King Avatar asked Aug 06 '15 23:08

Alexis King


1 Answers

Add this:

#main {
  min-height: 0;
  flex: 1 1 0;
}

html,
body {
  height: 100%;
  width: 100%;
}
body {
  margin: 0;
}
#root {
  display: flex;
  height: 100%;
}
#frame {
  display: flex;
  flex: 1;
  flex-direction: column;
}
#navigation-bar {
  background-color: #bab;
  display: flex;
  height: 70px;
  overflow: hidden;
}
#main {
  display: flex;
  flex: 1 1 0;
  min-height: 0;
}
#left-bar {
  background-color: #aaa;
  overflow: auto;
  width: 250px;
}
#content {
  background-color: #ccc;
  flex: 1;
}
<div id="root">
  <div id="frame">
    <div id="navigation-bar">
      <h1>Website Name</h1>
    </div>
    <div id="main">
      <div id="left-bar">
        <h1>Some content</h1>
        <h1>Some content</h1>
        <h1>Some content</h1>
        <h1>Some content</h1>
        <h1>Some content</h1>
        <h1>Some content</h1>
        <h1>Some content</h1>
        <h1>Some content</h1>
        <h1>Some content</h1>
      </div>
      <div id="content"></div>
    </div>
  </div>
</div>

You need min-height: 0 because, as explained in How can I get FF 33.x Flexbox behavior in FF 34.x?, the Flexbox module changes the initial value of min-height:

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.

I also added flex: 1 1 0 because flex: 1 becomes flex: 1 1 0%, but that 0% is buggy on Chrome in a column layout. But 0 works well.

like image 75
Oriol Avatar answered Sep 30 '22 10:09

Oriol