Reputation: 16696
I am building a modal, and want it to be centred in my page and grow organically but only until it reaches a certain width and height.
I center this modal (.modal
) with flexbox, and it works fine.
Inside this modal I have another flexbox (.modal-content
), with a body (.modal-content__body
) and a footer (.modal-content__footer
) element. I want the body to grow until the whole .modal
reaches my max-height
, at which point I want the modal-content__body
to start scrolling vertically.
The HTML hierarchy is:
.modal-container // Full screen
.modal // Centred box with max-width and max-height
.modal-content // The content of the modal
.modal-content__body // Potentially growing content
.modal-content__footer
I can't get the .modal-content
not to overflow. It simply ignores the parent's (.modal
) height and grows to provide space for the content.
If I remove the additional div of the .modal-content
and simply merge the .modal
with the .modal-content
it works as expected, but because I'm using a framework (Angular) I can't change the way the HTML elements are encapsulated.
(NOTE: If I set max-height: -webkit-fill-available;
on the .modal-content
it works, but this property is not well supported so I can't use it)
Here is a code demonstrating the issue:
/* Centers the child */
.modal-container {
display: flex;
align-items: center;
justify-content: center;
}
/* The box in the middle, that should grow
organically, but be restricted as well */
.modal,
.modal-content--merged-with-modal {
width: 30rem;
max-width: calc(50vw - 3rem);
max-height: calc(100vh - 3rem);
/* Irrelevant styling */
background: rgba(255, 255, 255, 0.6);
border-radius: 3px;
line-height: 1.5rem;
}
.modal-content {
display: flex;
flex-direction: column;
min-height: 0;
}
.modal-content__body {
flex: 1;
min-height: 0;
overflow-y: scroll;
/* Irrelevant styling */
padding: 0.75rem;
background: rgba(0, 0, 255, 0.1);
}
.modal-content__footer {
/* Irrelevant styling */
border-top: 1px solid silver;
background: rgba(0, 255, 0, 0.1);
padding: 0.75rem;
}
/* Irrelevant styling to present the problem */
html {
font-family: arial, sans-serif;
font-size: 16px;
}
main {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
}
section {
flex: 1;
background: rgba(255, 0, 0, 0.1);
}
<main>
<section class="modal-container">
<div class="modal-content modal-content--merged-with-modal">
<div class="modal-content__body">
<code>BODY // </code> srollable<br>
<br> Container resizes as expected when<br> the container height is reduced.<br>
<br> In this case, the modal is the same<br> element, as the flexbox handling<br> the body and the footer.<br> .
<br> .
<br> .
<br> .
<br> .
<br> .
<br>
</div>
<div class="modal-content__footer">
<code>FOOTER // </code> Works
</div>
</div>
</section>
<section class="modal-container">
<div class="modal">
<div class="modal-content">
<div class="modal-content__body">
<code>BODY // </code>simply grows<br>
<br> Container overflows when<br> the container height is reduced.<br>
<br> In this case, the modal <strong>contains</strong><br> the flexbox, that handles the<br> body and the footer.<br> .
<br> .
<br> .
<br> .
<br> .
<br> .
<br>
</div>
<div class="modal-content__footer">
<code>FOOTER // </code> Doesn't work
</div>
</div>
</div>
</section>
</main>
Upvotes: 3
Views: 3317
Reputation: 371193
In order for the overflow
property to work, it needs a fixed length limitation.
From MDN:
In order for
overflow
to have an effect, the block-level container must have either a set height (height
ormax-height
) orwhite-space
set tonowrap
.
In your code, there is no height limitation on .modal-content__body
(where the overflow
is set):
.modal-content__body {
flex: 1;
min-height: 0;
overflow-y: scroll;
}
Nor is there a height limitation on the parent:
.modal-content {
display: flex;
flex-direction: column;
min-height: 0;
}
flex-basis: 0
– the relevant component of flex: 1
in .modal-content__body
– isn't enough to trigger the overflow condition. But look what happens if you switch to flex-basis: 100px
/* Centers the child */
.modal-container {
display: flex;
align-items: center;
justify-content: center;
}
/* The box in the middle, that should grow
organically, but be restricted as well */
.modal, .modal-content--merged-with-modal {
width: 30rem;
max-width: calc(50vw - 3rem);
max-height: calc(100vh - 3rem);
/* Irrelevant styling */
background: rgba(255, 255, 255, 0.6);
border-radius: 3px;
line-height: 1.5rem;
}
.modal-content {
display: flex;
flex-direction: column;
min-height: 0;
}
.modal-content__body {
flex: 1 0 100px; /* new for demo 1 */
min-height: 0;
overflow-y: scroll;
/* Irrelevant styling */
padding: 0.75rem;
background: rgba(0, 0, 255, 0.1);
}
.modal-content__footer {
/* Irrelevant styling */
border-top: 1px solid silver;
background: rgba(0, 255, 0, 0.1);
padding: 0.75rem;
}
/* Irrelevant styling to present the problem */
html {
font-family: arial, sans-serif;
font-size: 16px;
}
main {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
}
section {
flex: 1;
background: rgba(255, 0, 0, 0.1);
}
<main>
<section class="modal-container">
<div class="modal">
<div class="modal-content">
<div class="modal-content__body">
<code>BODY // </code>simply grows<br>
<br> Container overflows when<br> the container height is reduced.<br>
<br> In this case, the modal <strong>contains</strong><br> the flexbox, that handles the<br> body and the footer.<br> .
<br> .
<br> .
<br> .
<br> .
<br> .
<br>
</div>
<div class="modal-content__footer">
<code>FOOTER // </code> Doesn't work
</div>
</div>
</div>
</section>
</main>
The scrolling works. A height limitation made the difference.
Here's a potential solution:
Since the height of .modal
(the parent) and modal-content
(the child) are the same, move the dimensions from the parent to the child. This brings the height limitation close enough to the content items to trigger the overflow.
Instead of this:
.modal {
width: 30rem;
max-width: calc(50vw - 3rem);
max-height: calc(100vh - 3rem);
}
.modal-content {
display: flex;
flex-direction: column;
min-height: 0;
}
Try this:
.modal { }
.modal-content {
display: flex;
flex-direction: column;
min-height: 0;
width: 30em;
max-width: calc(50vw - 3rem);
max-height: calc(100vh - 3rem);
}
/* Centers the child */
.modal-container {
display: flex;
align-items: center;
justify-content: center;
}
/* The box in the middle, that should grow
organically, but be restricted as well */
.modal, .modal-content--merged-with-modal {
/* Irrelevant styling */
background: rgba(255, 255, 255, 0.6);
border-radius: 3px;
line-height: 1.5rem;
}
.modal-content {
display: flex;
flex-direction: column;
min-height: 0;
width: 30rem;
max-width: calc(50vw - 3rem);
max-height: calc(100vh - 3rem);
}
.modal-content__body {
flex: 1;
min-height: 0;
overflow-y: scroll;
/* Irrelevant styling */
padding: 0.75rem;
background: rgba(0, 0, 255, 0.1);
}
.modal-content__footer {
/* Irrelevant styling */
border-top: 1px solid silver;
background: rgba(0, 255, 0, 0.1);
padding: 0.75rem;
}
/* Irrelevant styling to present the problem */
html {
font-family: arial, sans-serif;
font-size: 16px;
}
main {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
}
section {
flex: 1;
background: rgba(255, 0, 0, 0.1);
}
<main>
<section class="modal-container">
<div class="modal">
<div class="modal-content">
<div class="modal-content__body">
<code>BODY // </code>simply grows<br>
<br>
Container overflows when<br>
the container height is reduced.<br>
<br>
In this case, the modal <strong>contains</strong><br>
the flexbox, that handles the<br>
body and the footer.<br>
.<br>
.<br>
.<br>
.<br>
.<br>
.<br>
</div>
<div class="modal-content__footer">
<code>FOOTER // </code> Doesn't work
</div>
</div>
</div>
</section>
</main>
Upvotes: 1
Reputation: 272817
A simple fix is to make the .modal
a flex container with column direction.
/* Centers the child */
.modal-container {
display: flex;
align-items: center;
justify-content: center;
}
/* The box in the middle, that should grow
organically, but be restricted as well */
.modal, .modal-content--merged-with-modal {
width: 30rem;
max-width: calc(50vw - 3rem);
max-height: calc(100vh - 3rem);
/* Irrelevant styling */
background: rgba(255, 255, 255, 0.6);
border-radius: 3px;
line-height: 1.5rem;
}
.modal-content {
display: flex;
flex-direction: column;
min-height: 0;
}
.modal-content__body {
flex: 1;
min-height: 0;
overflow-y: scroll;
/* Irrelevant styling */
padding: 0.75rem;
background: rgba(0, 0, 255, 0.1);
}
.modal-content__footer {
/* Irrelevant styling */
border-top: 1px solid silver;
background: rgba(0, 255, 0, 0.1);
padding: 0.75rem;
}
.modal {
display:flex;
flex-direction:column;
}
/* Irrelevant styling to present the problem */
html {
font-family: arial, sans-serif;
font-size: 16px;
}
main {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
}
section {
flex: 1;
background: rgba(255, 0, 0, 0.1);
}
<main>
<section class="modal-container">
<div class="modal-content modal-content--merged-with-modal">
<div class="modal-content__body">
<code>BODY // </code> srollable<br>
<br>
Container resizes as expected when<br>
the container height is reduced.<br>
<br>
In this case, the modal is the same<br>
element, as the flexbox handling<br>
the body and the footer.<br>
.<br>
.<br>
.<br>
.<br>
.<br>
.<br>
</div>
<div class="modal-content__footer">
<code>FOOTER // </code> Works
</div>
</div>
</section>
<section class="modal-container">
<div class="modal">
<div class="modal-content">
<div class="modal-content__body">
<code>BODY // </code>simply grows<br>
<br>
Container overflows when<br>
the container height is reduced.<br>
<br>
In this case, the modal <strong>contains</strong><br>
the flexbox, that handles the<br>
body and the footer.<br>
.<br>
.<br>
.<br>
.<br>
.<br>
.<br>
</div>
<div class="modal-content__footer">
<code>FOOTER // </code> Doesn't work
</div>
</div>
</div>
</section>
</main>
Why?
Initially the .modal
was a block element with a max-height
set and the default overflow behavior (when there is an overflow) is visible
and you are facing an overflow issue.
By making the .modal
a flex container, you introduce the shrink effect as by default the flex item will have flex-shrink:1
so its height won't go beyond the height of the flex container (it will shrink to fit) and you will have the need result.
You may set flex-shrink:0
to .modal-content
and you will see the same overflow issue as if there is no display:flex
.
Upvotes: 3