'How to enable a scrollbar on a flex item?
I have a flexbox-based layout with two panels (top and bottom) occupying 1/3 and 2/3 of the viewport, respectively. (Actually there are more panels, but I've distilled it to the minimal example).
The top panel is also a flex container, because I want its children to flow top to bottom and be vertically centered when there is room. When there is more stuff in top panel than would fit in it, I want it to be scrollable, hence overflow: auto
.
The problem: the contents of top
shrink to its size, even with flex-shrink: 0
, and the scrollbar never pops up.
Observe how the content
is shrunk in the following demo, even though it has an explicitly specified height:
html, body {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
.main {
display: flex;
height: 100%;
flex-direction: column;
}
.top {
display: flex;
flex-direction: column;
flex-basis: 33%;
border-bottom: 1px solid #ccc;
overflow: auto;
justify-content: center;
padding: 20px;
}
.bottom {
overflow: auto;
flex-basis: 67%;
}
.content {
height: 500px;
background-color: #eee;
}
<div class="main">
<div class="top">
<div class="content">Content</div>
</div>
<div class="bottom"></div>
</div>
The questions:
How to fix this while preserving the layout requirements? Disabling
display: flex
for the top panel gives the desired effect in the demo. I could position contents oftop
in a flexboxless way, but I lose the benefits of flex layout and the automatic vertical centering.Why does this happen? References to CSS spec would be welcome.
Solution 1:[1]
You wrote:
The problem: the contents of
top
shrink to its size, even withflex-shrink: 0
, and the scrollbar never pops up.
Actually, the solution is flex-shrink: 0
. So the question becomes, where did you apply it?
It wouldn't work if you applied it to top
– a flex item in the primary container with flex-basis: 33%
(i.e., height: 33%
, in this case) – because the length of top
is a percentage. As such, it will shrink / expand naturally as percentage lengths are relative to the parent container.
You need to apply flex-shrink: 0
to .content
– a flex item in the nested container with a fixed height (height: 500px
/ flex-basis: 500px
).
So this will work:
.content {
height: 500px;
flex-shrink: 0;
}
or this:
.content {
flex-basis: 500px;
flex-shrink: 0;
}
or, better yet, this:
.content {
flex: 0 0 500px; /* don't grow, don't shrink, stay fixed at 500px */
}
From the spec:
7.2. Components of Flexibility
Authors are encouraged to control flexibility using the
flex
shorthand rather than with its longhand properties directly, as the shorthand correctly resets any unspecified components to accommodate common uses.
body {
margin: 0;
}
.main {
display: flex;
flex-direction: column;
height: 100vh;
}
.top {
display: flex;
flex-direction: column;
flex-basis: 33%;
border-bottom: 1px solid #ccc;
overflow: auto;
justify-content: center;
padding: 20px;
}
.bottom {
overflow: auto;
flex-basis: 67%;
}
.content {
flex: 0 0 500px;
background-color: #eee;
}
<div class="main">
<div class="top">
<div class="content">Content</div>
</div>
<div class="bottom"></div>
</div>
Then you have a second problem, which is that the upper section of the top
element gets cut off and is inaccessible via scroll. This is caused by justify-content: center
on the container.
This is a known issue. It is solved by using flex auto
margins.
So instead of this:
.top {
display: flex;
flex-direction: column;
flex-basis: 33%;
border-bottom: 1px solid #ccc;
overflow: auto;
/* justify-content: center; <--- REMOVE */
padding: 20px;
}
do this:
.content {
flex: 0 0 500px;
margin: auto 0; /* top & bottom auto margins */
background-color: #eee;
}
body {
margin: 0;
}
.main {
display: flex;
flex-direction: column;
height: 100vh;
}
.top {
display: flex;
flex-direction: column;
flex-basis: 33%;
border-bottom: 1px solid #ccc;
overflow: auto;
/* justify-content: center; USE AUTO MARGINS ON FLEX ITEM INSTEAD */
padding: 20px;
}
.bottom {
overflow: auto;
flex-basis: 67%;
}
.content {
flex: 0 0 500px;
margin: auto 0;
background-color: #eee;
}
<div class="main">
<div class="top">
<div class="content">Content</div>
</div>
<div class="bottom"></div>
</div>
Here's a complete explanation:
Solution 2:[2]
The scrollbar appears when there are enough .content
element shrinked to their very minimal height (one line height in this case).
That's not really how things work with flex. height
is not strictly respected. If you still want to work with height
, you can fix this by setting a min-height
to .content
according to the minimum height you want for them.
Or you can instead set flex
on .content
(and get rid of height
):
css
flex: 100px 1 0;
Which will set a minimum height (flex-basis) of 100px, flex-grow at 1 so that it takes all the available space, and flex-shrink at 0 so that the element is always at least 100px tall.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 |