带有嵌套 flexbox 和滚动的整页

Full page with nested flexbox and scrolling

我正在尝试使用 flexbox 进行我认为是相当标准的全屏布局,但它的行为方式并不符合我的要求。我的目标是:

* {
  box-sizing: border-box;
}

html, body {
  margin: 0;
}

.page {
  width: 100vw;
  height: 100vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.topbar {
  background-color: lightgreen;
}

.vcontainer {
  flex-grow: 1;
  display: flex;
  flex-direction: row;
  min-height: 0;
}

.sidebar {
  overflow-y: auto;
  background-color: yellow;
}

.content {
  flex-grow: 1;
  overflow: auto;
  background-color: lightblue;
}

.footer {
  background-color: pink;
}
<div class="page">
  <nav class="topbar">
    top bar
  </nav>
  <div class="vcontainer">
    <nav class="sidebar">
      side<br/>side<br/>side
    </nav>
    <main class="content">
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
    </main>
  </div>
  <footer class="footer">
    footer
  </footer>
</div>

上面的内容对于完整的文档滚动变体很好用(尽管神奇的是将 min-height 添加到 vcontainer;在此之前它不起作用)——蓝色区域占据剩余 space,如果内容太长则可滚动。

在不改变外部样式的情况下(或者至少不以破坏上述场景的方式改变它),我希望能够 有时 放入一些 content 本质上有一个额外的动态高度顶栏和一个不会触发主要内容滚动条的滚动子部分。

* {
  box-sizing: border-box;
}

html, body {
  margin: 0;
}

.page {
  width: 100vw;
  height: 100vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.topbar {
  background-color: lightgreen;
}

.vcontainer {
  flex-grow: 1;
  display: flex;
  flex-direction: row;
  min-height: 0;
}

.sidebar {
  overflow-y: auto;
  background-color: yellow;
}

.content {
  flex-grow: 1;
  overflow: auto;
  background-color: lightblue;
}

.footer {
  background-color: pink;
}

.contentcontainer {
  display: flex;
  flex-direction: column;
  min-height: 0;
}

.contentsub {
  margin: 1rem;
  overflow-y: scroll;
  flex-grow: 1;
  background-color: orange;
}
<div class="page">
  <nav class="topbar">
    top bar
  </nav>
  <div class="vcontainer">
    <nav class="sidebar">
      side<br/>side<br/>side
    </nav>
    <main class="content">
      <div class="contentcontainer">
        <div>
          some additional heading content
        </div>
        <div class="contentsub">
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
        </div>
        <div>
          some additional footer content
        </div>
      </div>
    </main>
  </div>
  <footer class="footer">
    footer
  </footer>
</div>

在这种情况下,我想要的是蓝色区域没有滚动条,而是橙色区域可以滚动,被额外的 header/footer 包围并且大小可以填充剩余的 space。理想情况下,即使 contentsub 不是 contentcontainer 的直接子元素,但在到达可滚动元素之前有更多的环绕元素和边框等,这也应该有效。我更喜欢“纯 flexbox”的解决方案,但如果有更好的方法,那么我会洗耳恭听。但是,我确实想避免使用 javascript。

经过一些调整后我得到了这个:

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
}

.page {
  width: 100vw;
  height: 100vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.topbar {
  background-color: lightgreen;
}

.vcontainer {
  flex-grow: 1;
  display: flex;
  flex-direction: row;
  min-height: 0;
}

.sidebar {
  overflow-y: auto;
  background-color: yellow;
}

.content {
  flex-grow: 1;
  overflow: auto;
  background-color: lightblue;
}

.footer {
  background-color: pink;
}

.contentcontainer {
  height: 100%;
}

.contentsub {
  max-height: max(40%, 2rem);
  margin: 1rem;
  overflow-y: scroll;
  background-color: orange;
}
<div class="page">
  <nav class="topbar">
    top bar
  </nav>
  <div class="vcontainer">
    <nav class="sidebar">
      side<br/>side<br/>side
    </nav>
    <main class="content">
      <div class="contentcontainer">
        <div>
          some additional heading content
        </div>
        <div class="contentsub">
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
          <p>some actual content</p>
        </div>
        <div>
          some additional footer content
        </div>
      </div>
    </main>
  </div>
  <footer class="footer">
    footer
  </footer>
</div>

网格方法:

* {
  box-sizing: border-box;
}

html,
body,
.page {
  height: 100%;
  margin: 0;
}

.topbar {
  grid-area: header;
  background-color: lightgreen;
}

.sidebar {
  grid-area: sidebar;
  overflow-y: auto;
  background-color: yellow;
}

.content {
  grid-area: content;
  display: flex;
  flex-flow: column nowrap;
  overflow: auto;
  background-color: lightblue;
}

.footer {
  grid-area: footer;
  background-color: pink;
}

.contentsub {
  flex-grow: 1;
  margin: 1rem;
  overflow-y: scroll;
  background-color: orange;
}

@media (min-width: 30rem) {
  .page {
    display: grid;
    grid-template-areas: "header header" "sidebar content" "footer footer";
    grid-template-columns: 1fr 9fr;
  }
}
<div class="page">
  <header class="topbar">
    top bar
  </header>
  <aside class="sidebar">
    side<br/>side<br/>side
  </aside>
  <main class="content">
    <div>
      some additional heading content
    </div>
    <div class="contentsub">
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
      <p>some actual content</p>
    </div>
    <div>
      some additional footer content
    </div>
  </main>
  <footer class="footer">
    footer
  </footer>
</div>