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 of top 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.
You wrote:
The problem: the contents of top shrink to its size, even with flex-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:
Can't scroll to top of flex item that is overflowing container
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.
Related
I have a problem, I need a form that will be limited in width and use the gap.
CodePen
body {
margin: 0;
padding: 0;
}
.container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 18px;
min-height: 100vh;
}
.container--example {
background-color: #000;
width: 100%;
height: 100px;
}
<div class="container">
<div class="container--example"></div>
<div class="container--example"></div>
<div class="container--example"></div>
<div class="container--example"></div>
</div>
Here the div is stretched to 100%, the whole screen, I can set the width settings on the element in the container, but then gap will not work.
Thank you so much in advance for your answer
Unfortunately, I do not know how to fix this problem, I hope to find the answer here.
UPD: Also, it is possible to put widths on the text and so on, but it's too much of a pain.
Set a padding to the parent of .container, in the example that would be body.
CSS body { padding: 0 2rem } would create a space of 2rem on either side of .container, which would be true for every child of body.
With a little math you can even make that space responsive:
16px space on a 320px viewport
256px space on a 1920px viewport
would yield equation y = 0.15x + 32 using Linear Equation y=mx+b for points p1(320,16) and p2(1920,256).
body {
margin: 0;
/* Using y=mx+b => y = 0.15x + 32 for points p1(320,16) and p2(1920,256) */
padding: 0 calc(15vw - 2rem);
}
.container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 18px;
max-width: ;
min-height: 100vh;
}
.container--example {
background-color: #000;
width: 100%;
height: 100px;
}
<div class="container">
<div class="container--example"></div>
<div class="container--example"></div>
</div>
If I set #main.overflow is none, I get what I want.
And if I set #main.overflow is scroll, which is not what I want, the last element is broken by error.
Why do scroll bars cause Flex layout errors?
right result
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
#main {
overflow: none;
width: 200px;
background-color: #321;
display: flex;
flex-direction: row;
padding: 10px;
flex-wrap: wrap;
justify-content: flex-start;
}
.el {
width: 50px;
height: 50px;
background-color: red;
margin: 5px;
}
<div id="main">
<div class="el"></div>
<div class="el"></div>
<div class="el"></div>
</div>
incorrect result
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
#main {
overflow: scroll;
width: 200px;
background-color: #321;
display: flex;
flex-direction: row;
padding: 10px;
flex-wrap: wrap;
justify-content: flex-start;
}
.el {
width: 50px;
height: 50px;
background-color: red;
margin: 5px;
}
<div id="main">
<div class="el"></div>
<div class="el"></div>
<div class="el"></div>
</div>
The simple answer is: The scrollbars take up space inside the div. If you force them to render by setting overflow: scroll you take up some of the space inside the flexbox and flex: wrap property causes the content to wrap.
Your main flexbox can handle content of 180px width*.
The content width is also exactly 180px**
This allows no space for the scrollbars and the content ends up wrapping onto the next line.
There is a deprecated property: overflow: overlay which causes the scrollbars to not take up space but it has very limited support and is deprecated so I would not recommend using it.
*Because the width is set to 200px which includes 10px padding on each side. The box-sizing: border-box property makes it so the padding is included in the height and width calculation
**Each el has 60px width including the margin on both sides.
I can't figure out for the life of me how to make this page work.
I'm trying to have the "Top" be a header, the "Bottom" be the footer, and "table" and "section" be two separate columns in between.
Although I can't figure it out. Thanks.
html {
height: 100%;
}
body {
display: flex;
height: 100%;
justify-content: flex-start;
align-content: flex-start;
flex-direction: column;
flex-wrap: wrap;
margin: 0;
}
#pageTop {
background-color: lightgrey;
padding-left: 1em;
padding-top: .5em;
flex-grow: 1;
}
#table {
background-color: blue;
width: 50%;
flex-grow: 8;
flex-shrink: 1;
}
#pageSection {
background-color: lightpink;
width: 50%;
flex-flow: 8;
flex-shrink: 1;
}
#pageBot {
flex-grow: 1;
background-color: grey;
}
<body>
<div id="pageTop">Top</div>
<nav id="table">table</nav>
<div id="pageSection">section</div>
<div id="pagebot">Bottom</div>
</body>
Like Micheal_B stated:
Wrap the #table and the #section in one container. That container becomes the second flex item in the parent flex container. Then add display: flex to the new container.
Changes
Added main#pageContent to body and wrapped it around nav#table and section#pageSection
Added display: flex, justify-content: center, and flex: 2 0 auto
Changed all flex-grow and flex-shrink to flex shorthand.
ex. flex: 0 1 auto = flex-grow: 0 flex-shrink: 1 flex-basis: auto
note. The ruleset above is default for all flex children.
Removed align-content and justify-content; and changed the value of flex-wrap from wrap to nowrap; and added overflow:hidden and width: 100% to normalize a little.
Added width: 100% to everything with the exception of #pageSection and #table.
Added height: 2em to #pageTop and #pageBot(BTW, corrected typo)
Changed all of the tags to it's semantic equivalents.
main#pageContent
Height is set up to take up the freespace that the footer and header leave by height: calc(100% - 4em). This probably overkill since it also has flex: 2 0 auto.
It is a flex container (flex: display) and a flex child (flex: 2 0 auto)
section#pageSection
overflow-x: hidden will prevent any content from busting out of the borders sideways. overflow-y:auto will accommodate any content that extends the bottom border by adding a scrollbar. I have added content (a few <p>) to demonstrate.
SNIPPET
html {
height: 100%;
width: 100%;
}
body {
display: flex;
height: 100%;
width: 100%;
flex-direction: column;
flex-wrap: nowrap;
margin: 0;
overflow: hidden;
}
#pageContent {
width: 100%;
height: calc(100% - 4em);
display: flex;
justify-content: center;
flex: 2 0 auto;
}
#pageTop {
width: 100%;
height: 2em;
background-color: violet;
padding-left: 1em;
padding-top: .5em;
flex: 0 0 auto;
}
#table {
background-color: cornflowerblue;
width: 50%;
flex: 0 0 auto;
}
#pageSection {
background-color: darksalmon;
width: 50%;
flex: 1 0 auto;
overflow-x: hidden;
overflow-y: auto;
}
#pageBot {
width: 100%;
height: 2em;
flex: 0 0 auto;
background-color: gold;
}
<body>
<header id="pageTop">Top</header>
<main id='pageContent'>
<nav id="table">table</nav>
<section id="pageSection">
<p>One morning, when Gregor Samsa woke from troubled dreams, he found himself transformed in his bed into a horrible vermin.</p>
<p>He lay on his armour-like back, and if he lifted his head a little he could see his brown belly, slightly domed and divided by arches into stiff sections. The bedding was hardly able to cover it and seemed ready to slide off any moment.</p>
<p>His many legs, pitifully thin compared with the size of the rest of him, waved about helplessly as he looked. "What's happened to me? " he thought. It wasn't a dream.</p>
<p>His room, a proper human room although a little too small, lay peacefully between its four familiar walls.</p>
<p>A collection of textile samples lay spread out on the table - Samsa was a travelling salesman - and above it there hung a picture that he had recently cut out of an illustrated magazine and housed in a nice, gilded frame. It showed a lady fitted
out with a fur hat and fur boa who sat upright, raising a heavy fur muff that covered the whole of her lower arm towards the viewer. Gregor then turned to look out the window at the dull weather. Drops</p>
</section>
</main>
<footer id="pageBot">Bottom</footer>
</body>
Add a div with flex row, as it (adjust cols width with width attribute):
html {
height: 100%;
}
body {
display: flex;
height: 100%;
justify-content: flex-start;
align-content: flex-start;
flex-direction: column;
flex-wrap: wrap;
margin: 0;
}
#pageTop {
background-color: lightgrey;
padding-left: 1em;
padding-top: .5em;
}
#mainContainer {
display: flex;
flex-direction: row;
}
#table {
background-color: blue;
width: 50%;
}
#pageSection {
background-color: lightpink;
width: 50%;
}
#pagebot {
background-color: grey;
}
<body>
<div id="pageTop">Top</div>
<div id="mainContainer">
<nav id="table">table</nav>
<div id="pageSection">section</div>
</div>
<div id="pagebot">Bottom</div>
</body>
PS: I also fixed a pagebot/pageBot variant. Be aware, CSS is case-sensitive.
I have a web page using a column flexbox, with fixed size header and footer, and a content area which takes up the remaining space. This works fine.
The content area is a row flexbox, and I have 2 square divs side by side. I am making them square by using padding-bottom. This works fine, unless the window is >2x the content area height. Then my squares start bleeding into the footer, because padding is based on element width.
I would like the squares to never overlap the footer. I'm ok with there just being dead space to the right of the squares. I would like to stick with flexbox and avoid floats if possible. Only modern browsers need be supported.
Is this possible with only CSS? Or is this a job for JS.
Fiddle
html, body {
height: 100%;
margin: 0;
padding: 0;
}
#box {
display: flex;
flex-flow: column;
height: 100%;
}
div {
border: 1px solid tomato;
box-sizing: border-box;
margin: 0;
padding: 2px;
}
#header {
flex: 0 0 5em;
}
#footer {
flex: 0 0 5em;
}
#content {
background: blue;
display: flex;
flex: 1 1 auto;
flex-flow: row wrap;
min-height: 30%;
}
#content > div {
background: tomato;
border-color: black;
flex: 1 0 auto;
max-height: 50%;
padding-bottom: 50%;
}
<div id="box">
<div id="header">
<p><b>header</b>
</p>
</div>
<div id="content">
<div id='am'></div>
<div id='pm'></div>
</div>
<div id="footer">
<p><b>footer</b>
</p>
</div>
</div>
TIA!
Simple soluton:
#box
{
display: flex;
flex-flow: column;
min-height: 100%; /* this*/
}
JSfiddle Demo
Note: This assumes you want the page to overflow...but I didn't see any reference to containing the page height to the viewport.
I have:
.container {
display:flex;
align-items:center
}
.content {
flex-grow:1
}
in order to align the .content div vertically with css only. The content changes dynamically and that's why I can't use position:absolute; margin-top:50%... styling. Because I never know the exact height of div on each content update.
But in a scenario where .container width changes but height remains, .content overflows .container top because it wraps the text within.
What I'm trying to do is never let .content exceed the top position less than 0. Even the most ideal situation will be preserving the padding-top value of .container and margin-top value of .content. Overflowing bottom will be OK, in fact it'll be my preference.
Any workarounds?
You can make .container height flexible: use min-height instead of height.
.container {
display: flex;
align-items:center;
min-height: 75px;
border: 3px solid blue;
}
.content {
height: 150px;
flex-grow: 1;
border: 3px solid red;
resize: vertical;
overflow: auto;
}
<div class="container">
<div class="content"></div>
</div>
Another possibility is using overflow: auto. However, instead of centering using align-items:center, use margin: auto 0.
.container {
display: flex;
height: 75px;
border: 3px solid blue;
overflow: auto;
resize: vertical;
}
.content {
height: 150px;
flex-grow: 1;
margin: auto 0;
border: 3px solid red;
}
<div class="container">
<div class="content"></div>
</div>