3

In the example below, on the desktop view, there's extra space at the bottom of the .main-content element that I'd like to remove. I have a feeling that it is related to height: 100% and flex-basis but not sure. flex-shrink doesn't work, and even if it did it might mess with the layout.

Could someone please explain how the browser is interpreting this layout and creating the extra space.

html,
body {
  height: 100%;
}

body {
  display: flex;
  flex-direction: column;
  margin: 0;
}

main {
  flex-grow: 1;
}

.container {
  display: flex;
  flex-direction: column;
  flex-wrap: wrap;
  height: 100%;
}

header,
footer,
[class^="sidebar-"],
.main-content {
  box-sizing: border-box;
  padding: 1rem;
}

header,
footer {
  background-color: lightgreen;
}

.sidebar-nav {
  background-color: lightblue;
}

.sidebar-content {
  background-color: lightyellow;
}

.sidebar-right {
  background-color: lightpink;
}

.main-content {
  background-color: lightgray;
}

@media( min-width: 48em) {
  .sidebar-nav,
  .sidebar-content {
    width: 26%;
    margin-right: 8%;
  }
  .main-content,
  .sidebar-right {
    flex-basis: 100%;
  }
  .main-content {
    width: 66%;
  }
  .sidebar-right {
    margin-left: 2rem;
    width: 20%;
  }
  .sidebar-right+.main-content {
    width: calc( 66% - ( 20% + 2rem));
  }
}


/* Ordering of Page Layout */

.sidebar-nav {
  order: 1;
}

.main-content {
  order: 2;
}

.sidebar-content {
  order: 3;
}

.sidebar-right {
  order: 4;
}

@media( min-width: 48em) {
  .sidebar-content {
    order: 2;
  }
  .main-content {
    order: 3;
  }
}
<header>
  Header
</header>

<main>
  <div class="container">

    <div class="sidebar-nav">
      Sidebar Navigation
    </div>
    <div class="sidebar-content">
      Sidebar Content
    </div>
    <div class="sidebar-right">
      Right Sidebar
    </div>
    <div class="main-content">
      Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br>      Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br>      Main Content<br> Main Content<br> Main Content
    </div>

  </div>
</main>

<footer>
  Footer
</footer>
hungerstar
  • 21,206
  • 6
  • 50
  • 59
  • 1
    Just for clarification, are you only referring to the desktop view? The mobile view appears to render how you would expect. – disinfor Oct 07 '19 at 16:45
  • 2
    Yes, that is correct. – hungerstar Oct 07 '19 at 16:49
  • 3
    I'm not posting as an answer, because I'm not positive, but I think you are right in your suspicion that `flex-basis: 100%` is the culprit. You have your container set to `flex-direction: column` and `height: 100%`, so by that logic, the elements that have `flex-basis: 100%` will expand to 100% of the space available. On another note, this may just be a test case and not actual use, but it seems that `grid` might be a better layout choice for this. – disinfor Oct 07 '19 at 16:52
  • I agree with disinfor, flex-direction: column seems to be the culprit instead. – duc mai Oct 07 '19 at 17:26
  • I'm pretty sure I understood your question, but if I missed something in my answer just let me know. – Michael Benjamin Oct 07 '19 at 18:35
  • Here's some fun to play with: https://codepen.io/chrislafrombois/pen/vYYExyd What I found is that if the `flex-direction` is set to column and the outer parent has an explicit height set, then the `flex-basis` property will apply vertically (as you could guess with any `height` value). You can set the `flex-grow` to 0 for the main content area and it will only be as tall as the content. If we reduce the actual text content in the main area, it will render under the two left sidebar elements. – disinfor Oct 07 '19 at 18:44

1 Answers1

1

Solution

Add min-height: 0 or overflow: auto (they're mostly the same) to main and .main-content.

main {
  flex-grow: 1;
  min-height: 0; /* new */
}

.main-content {
  order: 2;
  overflow: auto;  /* new */
}

Tested on Chrome, Firefox and Edge.

body {
  display: flex;
  flex-direction: column;
  height: 100vh;
  margin: 0;
}

main {
  flex-grow: 1;
  min-height: 0; /* new */
}

.container {
  display: flex;
  flex-direction: column;
  flex-wrap: wrap;
  height: 100%;
}

header,
footer,
[class^="sidebar-"],
.main-content {
  box-sizing: border-box;
  padding: 1rem;
}

header,
footer {
  background-color: lightgreen;
}

.sidebar-nav {
  background-color: lightblue;
}

.sidebar-content {
  background-color: lightyellow;
}

.sidebar-right {
  background-color: lightpink;
}

.main-content {
  background-color: lightgray;
}

@media(min-width: 48em) {
  .sidebar-nav,
  .sidebar-content {
    width: 26%;
    margin-right: 8%;
  }
  .main-content,
  .sidebar-right {
    flex-basis: 100%;
  }
  .main-content {
    width: 66%;
  }
  .sidebar-right {
    margin-left: 2rem;
    width: 20%;
  }
  .sidebar-right+.main-content {
    width: calc(66% - (20% + 2rem));
  }
}


/* Ordering of Page Layout */

.sidebar-nav {
  order: 1;
}

.main-content {
  order: 2;
  overflow: auto;   /* new */
}

.sidebar-content {
  order: 3;
}

.sidebar-right {
  order: 4;
}

@media(min-width: 48em) {
  .sidebar-content {
    order: 2;
  }
  .main-content {
    order: 3;
  }
}
<header>
  Header
</header>
<main>
  <div class="container">
    <div class="sidebar-nav">
      Sidebar Navigation
    </div>
    <div class="sidebar-content">
      Sidebar Content
    </div>
    <div class="sidebar-right">
      Right Sidebar
    </div>
    <div class="main-content">
      Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br>      Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br> Main Content<br>      Main Content<br> Main Content<br> Main Content
    </div>
  </div>
</main>
<footer>
  Footer
</footer>

jsFiddle demo


Problem #1: Flex minimum size algorithm

A flex item cannot be smaller than the size of its content along the main axis. A default setting for flex items in flex-direction: column is min-height: auto. You can override this with min-height: 0 or, if you want scroll bars, overflow: auto.

Full explanation here: Why don't flex items shrink past content size?


Problem #2: Unreliable use of percentage heights

Per the spec, for a percentage height to work as intended, it must have a defined height on the parent. For example, your .container element has this CSS:

.container {
  display: flex;
  flex-direction: column;
  flex-wrap: wrap;
  height: 100%;
}

However, the parent of .container, which is main, has no defined height.

main {
  flex-grow: 1;
}

This leads to inconsistent and unreliable behavior across browsers.

Without a defined height on the parent the browser should, according to the spec, render the element with height: auto, the default setting.

However, in reality, different browsers have different rendering behavior. Some stick to the spec. Others try to guess what the author intended (which is known as an intervention).

Also, some browsers nowadays accept flex lengths on parents as adequate references for children with percentage heights. It's inconsistent and unreliable, however.

The best way to handle this set-up is to make sure to set a height on the parent so that the child with a percentage height has a clear reference point. And also make sure to use the height property, as flex-basis isn't always reliable in some browsers / situations.

Full explanation here: Chrome / Safari not filling 100% height of flex parent

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701