I'm trying to do what I thought was a fairly standard sort of full-screen layout using flexbox, but it's not behaving the way I want. My goals are:
* {
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>
The above works fine for the full doc scrolling variant (although the magic was adding the min-height
to vcontainer
; it didn't work prior to that) -- the blue area takes the remaining space and is scrollable if the content gets too long.
Without changing the outer styling (or at least not changing it in a way that breaks the above scenario), I want to be able to sometimes put in some content
that essentially has an additional dynamic-height topbar and a scrolling subsection that doesn't trigger the main content scrollbar.
* {
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>
What I'm wanting is for the blue area to not have a scrollbar in this case, but instead for the orange area to be scrollable, surrounded by the extra header/footer and sized to fill the leftover space. Ideally, this should work even if contentsub
isn't a direct child of the contentcontainer
but there's more wrapping elements and borders etc before getting to the scrollable element. I would prefer a solution that's "pure flexbox", but if there's a better way to do it then I'm all ears. I do want to avoid using javascript, however.
I got this after a bit of tweaking:
* {
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>
Grid approach:
* {
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>