1

I'm trying to get a a layout where I have a main header, a sidebar, the main content, and a main content header, where the only scroll bars are in vertical direction for the sidebar and the vertical and horizontal directions of the main content.

In my current code, I am only able to get a vertical scroll bar on the body and a horizontal scroll bar on the content, and it is hard to access because the scroll bar for the content is at the very bottom of the body, which I don't want to grow in height.

What I'm having trouble figuring out is how to make the body fit to width and height of the screen, while the contents of #menu_bar and #main_content have the correct scroll bars.

Here is a mock up of what I am trying to achieve:

enter image description here

Here is what I have right now.

body {
  min-width: 800px;
  min-height: 500px;
  overflow: auto;
  margin: 10px;
  border: solid black 1px;
  max-width: 100%;
  min-height: 100%;
  font-family: Arial, Helvetica, sans-serif;
  user-select: none;
}

#content {
  position: relative;
  display: flex;
  flex-direction: row;
  width: 100%;
  height: 100%;
}

#menu_bar {
  position: relative;
  border-top: none;
  overflow-y: auto;
}

#tabs {
  width: 100%;
}

.tab {
  margin: 5px;
  padding: 5px;
  width: max-content;
  max-width: 300px;
}

#main {
  position: relative;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  width: 100%;
  height: 100%;
}

#main_header {
  display: flex;
  flex-direction: column;
}

#main_content {
  box-sizing: inherit;
  display: flex;
  flex-direction: column;
  position: relative;
  overflow-x: auto;
  overflow-y: auto;
  margin: 10px;
  max-height: 100%;
}

.vertical-divider {
  border-left: black solid 1px;
}

.horizontal-divider {
  border-top: black solid 1px;
}
<!DOCTYPE html>
<html lang="en">

<body>
  <header>
    PAGE HEADER
  </header>
  <div class="horizontal-divider" id="header_content_divider"></div>
  <div id="content">
    <div class="vertical-divider hidden" style="display: none;"></div>
    <!-- Left Menu Bar -->
    <div id="menu_bar">
      <div id="tabs">
        <div class="tab">Tab 0</div>
        <div class="horizontal-divider"></div>
        <div class="tab">Tab 1</div>
        <div class="horizontal-divider"></div>
        <div class="tab">Tab 2</div>
        <div class="horizontal-divider"></div>
        <div class="tab">Tab 3</div>
        <div class="horizontal-divider"></div>
        <div class="tab">Tab 4</div>
        <div class="horizontal-divider"></div>
        <div class="tab">Tab 5</div>
        <div class="horizontal-divider"></div>
        <div class="tab">Tab 6</div>
        <div class="horizontal-divider"></div>
        <div class="tab">Tab 7</div>
        <div class="horizontal-divider"></div>
        <div class="tab">Tab 8</div>
        <div class="horizontal-divider"></div>
        <div class="tab">Tab 9</div>
        <div class="horizontal-divider"></div>
        <div class="tab">Tab 10</div>
        <div class="horizontal-divider"></div>
        <div class="tab">Tab 11</div>
        <div class="horizontal-divider"></div>
        <div class="tab">Tab 12</div>
      </div>
    </div>
    <div class="vertical-divider" id="left_main_divider"></div>
    <div id="main">
      <div id="main_header">
        MAIN HEADER
      </div>
      <div class="horizontal-divider"></div>
      <div id="main_content">
        <div id="wide">
          ThisIsWideContentThisIsWideContentThisIsWideContentThisIsWideContentThisIsWideContentThisIsWideContentThisIsWideContentThisIsWideContentThisIsWideContentThisIsWideContentThisIsWideContentThisIsWideContent
        </div>
        <div id="tall">
          <p>ThisIsTallContent</p>
          <p>ThisIsTallContent</p>
          <p>ThisIsTallContent</p>
          <p>ThisIsTallContent</p>
          <p>ThisIsTallContent</p>
          <p>ThisIsTallContent</p>
          <p>ThisIsTallContent</p>
          <p>ThisIsTallContent</p>
          <p>ThisIsTallContent</p>
          <p>ThisIsTallContent</p>
          <p>ThisIsTallContent</p>
          <p>ThisIsTallContent</p>
          <p>ThisIsTallContent</p>
          <p>ThisIsTallContent</p>
          <p>ThisIsTallContent</p>
          <p>ThisIsTallContent</p>
          <p>ThisIsTallContent</p>
          <p>ThisIsTallContent</p>
          <p>ThisIsTallContent</p>
          <p>ThisIsTallContent</p>
          <p>ThisIsTallContent</p>
          <p>ThisIsTallContent</p>
        </div>
      </div>
    </div>

</body>

</html>
Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
loiuytre35
  • 82
  • 8

1 Answers1

1

To make this layout work you need to be familiar with at least five CSS concepts.

I'll post the code first, with the explanations at the end.

Code

body {
  display: flex;
  flex-direction: column; /* will help with triggering overflow 
                             (by allowing use of flex properties further in) */
  height: 100vh;
  margin: 0; /* override default margins */
  font-family: Arial, Helvetica, sans-serif;
}

header {
  padding: 10px;
  background-color: lightgray;   
  border: 1px solid black;
}

#content {
  display: flex; /* children line up in a row */
  height: 50vh;  /* hard limi t help trigger overflow condition, 
                    while leaving plenty of space for the header */
  flex-grow: 1;  /* consume remaining height */
}

#menu_bar {
  height: 100%;  /* inherit parent height; essential for overflow to trigger */
  flex: 0 1 300px; /* space for tabs; max-width taken from individual tabs rule */
}

#tabs {
  overflow: auto; /* first scrollbar requirement met */
  height: 100%;  /* inherit parent height; essential for overflow to trigger */
  background-color: lightgreen; /* demo */
}

.tab {
  padding: 10px;
  /* max-width: 300px; */ /* rule moved to container */
  border-bottom: 1px solid black;
}


#main {
  flex: 1; /* consume all space (so box width not dependent on content */
  display: flex;
  flex-direction: column;
  height: 100%;
  min-width: 0; /* override flex minimum width setting */
  border-left: 1px solid black;
}

#main_header {
  padding: 10px;
  background-color: yellow;
  border-bottom: 1px solid black;
}

#main_content {
  display: flex;
  flex-direction: column;
  height: 100%; /* can shrink to fit (flex-shrink: 1 default) */
  overflow: auto; /* second scrollbar requirement set */
  background-color: orange;
}

#wide {
  padding-top: 10px;
}

* {
  box-sizing: border-box;
}
<header>PAGE HEADER</header>
<div id="content">
  <div id="menu_bar">
    <div id="tabs">
      <div class="tab">Tab 0</div>
      <div class="tab">Tab 1</div>
      <div class="tab">Tab 2</div>
      <div class="tab">Tab 3</div>
      <div class="tab">Tab 4</div>
      <div class="tab">Tab 5</div>
      <div class="tab">Tab 6</div>
      <div class="tab">Tab 7</div>
      <div class="tab">Tab 8</div>
      <div class="tab">Tab 9</div>
      <div class="tab">Tab 10</div>
      <div class="tab">Tab 11</div>
      <div class="tab">Tab 12</div>
    </div>
  </div>
  <div id="main">
    <div id="main_header">MAIN HEADER</div>
    <div id="main_content">
      <div id="wide">
    ThisIsWideContentThisIsWideContentThisIsWideContentThisIsWideContentThisIsWideContentThisIsWideContentThisIsWideContentThisIsWideContentThisIsWideContentThisIsWideContentThisIsWideContentThisIsWideContent
      </div>
      <div id="tall">
        <p>ThisIsTallContent</p>
        <p>ThisIsTallContent</p>
        <p>ThisIsTallContent</p>
        <p>ThisIsTallContent</p>
        <p>ThisIsTallContent</p>
        <p>ThisIsTallContent</p>
        <p>ThisIsTallContent</p>
        <p>ThisIsTallContent</p>
        <p>ThisIsTallContent</p>
        <p>ThisIsTallContent</p>
        <p>ThisIsTallContent</p>
        <p>ThisIsTallContent</p>
        <p>ThisIsTallContent</p>
        <p>ThisIsTallContent</p>
        <p>ThisIsTallContent</p>
        <p>ThisIsTallContent</p>
        <p>ThisIsTallContent</p>
        <p>ThisIsTallContent</p>
        <p>ThisIsTallContent</p>
        <p>ThisIsTallContent</p>
        <p>ThisIsTallContent</p>
        <p>ThisIsTallContent</p>
      </div>
    </div>
  </div>

jsFiddle demo


Explanations

As mentioned in the intro, there are multiple CSS concepts that need to be understood for this layout to work.

  1. The overflow property needs a hard limit, so that it can actually overflow something. Without a fixed length, there's nothing to overflow.

  2. Flex items have a minimum size equal to the size of the content, which sometimes must be overridden for content to fit neatly in the viewport.

  3. Flex items can shrink, by default. This means you can set nested flex items to height: 100%, which can trigger and overflow without overflowing the container.

  4. The body element has a default margin set by the browser. You can override this setting with body { margin: 0 } and eliminate an unnecessary vertical scrollbar.

  5. You don't need to add dividers in the HTML. It's a lot of extra and unnecessary code. Just use CSS borders.

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
  • Thanks for the answer. This solves most of the issues, but two things are left. For `#main`, I want it to fill the whole width of the page even if `#main_content` isn't that wide. I tried adding `width: 100%`, but if the contents of `#main_content` is too wide, then elements I put at the end of the `#main_header` using `justify-content: flex-end` on a child will go off the screen, which I do not want (nor a scroll bar). Also, if the window is too small, `body` still has scroll bars. – loiuytre35 May 01 '20 at 22:01
  • 1
    Yeah, `width: 100%` won't do. Add `flex: 1` to `#main`. I'm not seeing the second problem. Code demos revised. – Michael Benjamin May 01 '20 at 22:18