skip to Main Content

So, I need a page with a 100%-width header above and two blocks – aside and main – that fit remaining height below it in the parent block (its height – 100vh) and both lay on a horizontal baseline. Main may contain too much content so it needs to be scrollable along Y axis – only content inside it needs to be scrollable, other parts (header and sidebar) remain in their places. So I have this code (question continues after it):

body {
  margin: 0;
}

.fx-a {
  height: 100vh;
  width: 100%;
  display: flex;
  flex-direction: column;
}

.header {
  background: red;
}

.fx-b {
  flex-grow: 1;
  display: flex;
}

.aside {
  background: yellow;
  flex: 1;
}

.main {
  background: blue;
  flex: 3;
  overflow-y: scroll;
}

.test {
  margin-top: 100px;
}
<div class="fx-a">
  <header class="header">
    <h2>Title</h2>
  </header>
  <div class="fx-b">
    <aside class="aside"></aside>
    <main class="main">
      <div class="test">a</div>
      <div class="test">a</div>
      <div class="test">a</div>
      <div class="test">a</div>
      <div class="test">a</div>
      <div class="test">a</div>
      <div class="test">a</div>
      <div class="test">a</div>
      <div class="test">a</div>
      <div class="test">a</div>
      <div class="test">a</div>
    </main>
  </div>
</div>

But when content in the main grows, main also grows, and sidebar does as well because it’s in the same flex box. Then, when I try to scroll, the whole page scrolls, that’s not what I need. How can it be fixed with only HTML and CSS?

P.S. I know I could set the height to header manually and then subtract it from 100vh to set the aside and main height, but I think it’s not the case – height of the header depends on content inside of it.

2

Answers


  1. Set overflow: hidden on .fx-b to force the container to limit its child:

    body {
      margin: 0;
    }
    
    .fx-a {
      height: 100vh;
      width: 100%;
      display: flex;
      flex-direction: column;
    }
    
    .header {
      background: red;
    }
    
    .fx-b {
      flex-grow: 1;
      display: flex;
      overflow: hidden;
    }
    
    .aside {
      background: yellow;
      flex: 1;
    }
    
    .main {
      background: blue;
      flex: 3;
      overflow-y: scroll;
    }
    
    .test {
      margin-top: 100px;
    }
    <div class="fx-a">
      <header class="header">
        <h2>Title</h2>
      </header>
      <div class="fx-b">
        <aside class="aside"></aside>
        <main class="main">
          <div class="test">a</div>
          <div class="test">b</div>
          <div class="test">c</div>
          <div class="test">d</div>
          <div class="test">e</div>
          <div class="test">f</div>
          <div class="test">g</div>
          <div class="test">h</div>
          <div class="test">i</div>
          <div class="test">j</div>
          <div class="test">k</div>
        </main>
      </div>
    </div>
    Login or Signup to reply.
  2. Add height: 1px to the container.

    .fx-a {
      height: 100vh;
      display: flex;
      flex-direction: column;
    }
    
    .header {
      background: red;
    }
    
    .fx-b {
      flex-grow: 1;
      display: flex;
      height: 1px;  /* NEW */
    }
    
    .aside {
      background: yellow;
      flex: 1;
      border: 3px dashed black; /* demo */
    }
    
    .main {
      background: blue;
      flex: 3;
      overflow-y: scroll;
    }
    
    .test {
      margin-top: 100px;
    }
    
    body {
      margin: 0;
    }
    <div class="fx-a">
      <header class="header">
        <h2>Title</h2>
      </header>
      <div class="fx-b">
        <aside class="aside"></aside>
        <main class="main">
          <div class="test">a</div>
          <div class="test">a</div>
          <div class="test">a</div>
          <div class="test">a</div>
          <div class="test">a</div>
          <div class="test">a</div>
          <div class="test">a</div>
          <div class="test">a</div>
          <div class="test">a</div>
          <div class="test">a</div>
          <div class="test">a</div>
        </main>
      </div>
    </div>

    For overflow scroll to work, there needs to be something to overflow.

    Without a fixed height (or width), the container simply expands with the content, and there’s no overflow.

    But with a fixed height (or width), a fixed limit is set. Once this limit is breached, an overflow condition is triggered, and the scrollbar can be activated.

    Now, of course, most people want overflow scroll with dynamic lengths. That’s why this is a common problem. How to get the scrollbar without setting a height (or width)?

    The answer is actually quite simple with the advent of flexbox.

    Give the browser what it wants: a fixed length.

    But make it meaningless to the layout, using flex-grow for the actual height.

    In other words: height: 1px; flex-grow: 1

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search