CSS Flexbox layout with unknown child height 100% - html

I'm having a problem with what I assumed would be a relatively simple CSS layout.
I want...
a footer at the bottom (fixed height)
a nav bar on the left side (fixed width)
an outer content pane on the right side (dynamic size)
And in the content pane...
a ribbon on the top (fixed height)
an inner content div (dynamic size)
I'm trying to use flexbox for this, as it seems to be the best option at present (grid is too unstable and not well supported).
The problem is that my inner content div is (in reality) several layers deep inside the outer content div. This means I cannot be using flexbox directly on it, as flex is a direct parent-child relationship (and I'm a few layers deep, as per the comment in the code).
Ideally I want to do it as my below code, however as you can see, the inner div is flowing OUTSIDE the containing outer div's box, behind the footer, and then outside the entire container (for the height of the ribbon div).
It would seem "height: 100%" just does not deal with it properly... but I can't have every item in the chain from "content" to "inner" set up with flexbox - as I literally do not have control over this. (I'm using angular, and it generates a bunch of tags every layer, and they're dynamically named).
body {
margin: 0px;
padding: 0px;
}
.container {
display: flex;
flex-direction: column;
height: 100%
}
.body {
display: flex;
flex: 1;
}
.content {
background-color: blue;
flex: 1;
}
.ribbon {
background-color: orange;
height: 99px;
}
.inner {
overflow: auto;
height: 100%;
}
.footer {
background-color: lime;
flex: 0 0 38px;
}
<body>
<div class="container">
<div class="body">
<div class="content">
<div class="ribbon">STUFF<br>BLAH</div>
<!-- there is actually a bunch of other stuff in between "content" and "inner"-->
<div class="inner">
content<br>content<br>content<br>content<br>content<br>content<br>content<br>content<br>
content<br>content<br>content<br>content<br>content<br>content<br>content<br>content<br>
content<br>content<br>content<br>content<br>content<br>content<br>content<br>content<br>
content<br>content<br>content<br>content<br>content<br>content<br>content<br>content<br>
content<br>content<br>content<br>content<br>content<br>content<br>content<br>content<br>
content<br>content<br>content<br>content<br>content<br>content<br>content<br>content<br>
content<br>content<br>content<br>content<br>content<br>content<br>content<br>content<br>
content<br>content<br>content<br>content<br>content<br>content<br>content<br>content<br>
content<br>content<br>content<br>content<br>content<br>content<br>content<br>content<br>
content<br>content<br>content<br>content<br>content<br>content<br>content<br>content<br>
content<br>content<br>content<br>content<br>content<br>content<br>content<br>content<br>
</div>
</div>
</div>
<div class="footer">footer</div>
</div>
</body>
I have made a plunker to demonstrate the problem...
https://plnkr.co/edit/zzFHhLK7nef1VNQkwp7U?p=preview
Any help would be greatly appreciated!

What I recommend is making a second layer of flexbox, by adding display: flex and flex-direction: column to .content, in addition to its existing flex: 1:
.content {
flex: 1;
display: flex;
flex-direction: column;
}
Considering flexbox is a parent-child relationship, you'll need to add flex: 1 to anything that is a direct child of .content, which can be done by using the child combinator >:
.content > * {
flex: 1;
}
In addition to this, you'll need to set a max-height on the .ribbon:
.ribbon {
max-height: 99px;
}
Combined together, this can be seen working in the following example:
body {
margin: 0px;
padding: 0px;
}
.container {
display: flex;
flex-direction: column;
height: 100%
}
.body {
display: flex;
flex: 1;
}
.content {
background-color: blue;
flex: 1;
display: flex;
flex-direction: column;
}
.content > * {
flex: 1;
}
.ribbon {
background-color: orange;
height: 99px;
max-height: 99px;
}
.inner {
overflow: hidden;
height: 100%;
}
.footer {
background-color: lime;
flex: 0 0 38px;
}
<body>
<div class="container">
<div class="body">
<div class="content">
<div class="ribbon">STUFF<br>BLAH</div>
<!-- there is actually a bunch of other stuff in between "content" and "inner"-->
<div class="inner">
content<br>content<br>content<br>content<br>content<br>content<br>content<br>content<br> content
<br>content<br>content<br>content<br>content<br>content<br>content<br>content<br> content
<br>content<br>content<br>content<br>content<br>content<br>content<br>content<br> content
<br>content<br>content<br>content<br>content<br>content<br>content<br>content<br> content
<br>content<br>content<br>content<br>content<br>content<br>content<br>content<br> content
<br>content<br>content<br>content<br>content<br>content<br>content<br>content<br> content
<br>content<br>content<br>content<br>content<br>content<br>content<br>content<br> content
<br>content<br>content<br>content<br>content<br>content<br>content<br>content<br> content
<br>content<br>content<br>content<br>content<br>content<br>content<br>content<br> content
<br>content<br>content<br>content<br>content<br>content<br>content<br>content<br> content
<br>content<br>content<br>content<br>content<br>content<br>content<br>content<br>
</div>
</div>
</div>
<div class="footer">footer</div>
</div>
</body>
Hope this helps! :)

Related

Dynamic size of footer with full screen web page

image wireframe
I would like to recreate messaging phone app in html and css. So the app must be full frame without any overflow.
The trick is the bottom part (in red) must be resizable according to the child content. So I used flex (with flex-direction: column) to manage my layout.
The problem is : when the content (in yellow) grow up, the core part will compress the red part. My goal is to overflow, with a scrollbar, the content inside the core part and don't change the size of the red div.
index.html
<body>
<div id="header">
</div>
<div id="core">
<div class="conainer" style="">
<div class="row">
<div class="two columns"></div>
<div class="ten columns">
<div class="msgright">
.
</div>
</div>
</div>
<div class="row">
<div class="ten columns">
<div class="msgright">
.
</div>
</div>
<div class="two columns"></div>
</div>
</div>
</div>
<div id="footer">
</div>
index.css
html, body, div {
margin: 0;
padding: 0;
}
html, body {
height: 100%;
max-height: 100%;
overflow: hidden;
}
body {
display: flex;
flex-direction: column;
}
#header {
height: 50px;
background: #2A9D8F;
flex: 0 0 auto;
}
#core {
background-color: #264653;
flex: 1;
display: flex;
align-items: flex-end;
}
#footer {
height: auto;
background-color: red;
min-height: 50px;
flex: 0 0 auto;
}
.conainer {
flex: 0 0 100%;
}
.row {
margin: 5px;
background-color: yellow;
height: 130px;
}
https://codepen.io/jln_brtn/pen/pobVZBv
Best regards and thank you for your help.
I'm not sure if I understand the problem correctly but since your .row elements have a fixed height: 130px, the element should not be able to grow any further. Overflow styling to .row elements can be added like this:
.row {
overflow-y: scroll;
}
If it is just the #core element, then you can do something like this:
#core {
overflow-y: scroll;
}
For this instance I would suggest to use CSS Grid instead of Flexbox, and giving both <header> and <footer> the space they need, while the <main> gets the rest. This means that both <header> and <footer> stay were they are, even if <main> needs more space for its content, meaning <main> will get a scrollbar.
You can achieve the same by using position: fixed and setting a margin to top and bottom, with fixed heights of <header> and <footer>, and sizing <main> with height: calc(100% - HEIGHT_OF_HEADER - HEIGHT_OF_FOOTER). The problem with this is maintenance, as you would always have to check and revalidate the heights when changing something.
html, body {
margin: 0;
width: 100%;
height: 100%;
}
body {
display: grid;
grid-template-rows: auto 1fr auto;
}
header {
height: 3.125rem;
background: #2A9D8F;
}
main {
padding: 0.3125rem;
display: flex;
flex-flow: column;
gap: 0.3125rem;
background: #264653;
overflow: hidden auto;
}
footer {
height: 3.125rem;
background: red;
}
main > div {
flex-shrink: 0;
height: 8.125rem;
background: yellow;
}
<header></header>
<main>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</main>
<footer></footer>

How to set flex column child to go full available height? [duplicate]

This question already has answers here:
Make a div fill the height of the remaining screen space
(41 answers)
Percentage Height HTML 5/CSS
(7 answers)
Flexbox fill available space vertically
(2 answers)
Closed 3 years ago.
Consider the following code:
.container {
display: flex;
flex-direction: column;
}
.header {
flex: 1;
background-color: red;
}
.content {
flex: 1;
background-color: blue;
height: 100%;
}
<div class="container">
<div class="header">
<h1>HEADER</h1>
</div>
<div class="content">
<h1>CONTENT</h1>
</div>
</div>
Why is my CONTENT not getting full available height (from HEADER bottom to the bottom of page) ? How to solve it ?
I'm putting this answer to clear up a few things mentioned in the comments, if it's not appropiate due to the question already having an answer I'll delete this.
By making the changes I proposed, we set the .container's height to 100vh, to explicitly define that it must have the full viewport's height, without this, the .container only has the needed height to contain the elements inside of it.
This applies the same to the body and html elements.
.container {
display: flex;
flex-direction: column;
height: 100vh;
}
.header {
flex: 1;
background-color: red;
}
.content {
flex: 1;
background-color: blue;
}
<div class="container">
<div class="header">
<h1>HEADER</h1>
</div>
<div class="content">
<h1>CONTENT</h1>
</div>
</div>
Using percentages to define a height or width require some reference to calculate how much space that % unit is; so for example:
If we set a width of 1000px for .container, we can set its children's width to say, 50% and 100% and they will resize accordingly to 500px and 1000px because they have the 1000px reference from their parent.
EDIT: As noted by #Temani, this reference is always present for the width property, so using percentages for width will never fail, even if we don't specify an explicit width in a parent container.
.container {
display: flex;
flex-direction: column;
width: 1000px;
}
.header {
flex: 1;
width: 50%;
background-color: red;
}
.content {
flex: 1;
background-color: blue;
width: 100%
}
<div class="container">
<div class="header">
<h1>HEADER</h1>
</div>
<div class="content">
<h1>CONTENT</h1>
</div>
</div>
The same happens with the height property; we define a specific
height for the parent, and the children's height can be set with percentages since now they have a reference.
.container {
display: flex;
flex-direction: column;
height: 500px;
}
.header {
height: 20%;
background-color: red;
}
.content {
background-color: blue;
height: 80%
}
<div class="container">
<div class="header">
<h1>HEADER</h1>
</div>
<div class="content">
<h1>CONTENT</h1>
</div>
</div>
Your container is missing an height , you can use height:100vh; to fill window's height.
You can also use % , but you need to inherit a valid value from a parent. In this case, it can be take from html, send to body, and finally used by your container:(example in this duplicate)
html,body,.container {height:100%;}
example with vh
body {
margin: 0;
}
.container {
display: flex;
flex-direction: column;
/*or min-height*/ height: 100vh;
}
.header {
/* flex: 1; not needed */
background-color: red;
}
.content {
flex: 1;
background-color: blue;
/*height: 100%; not needed */
}
<div class="container">
<div class="header">
<h1>HEADER</h1>
</div>
<div class="content">
<h1>CONTENT</h1>
</div>
</div>
Its going to depend on what you want to ultimately do the page and how you are going to use the page.
You can set your .container full page width & height:
.container {
display: flex;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
and then grow/shrink your containers as needed:
.header{ flex: 1 }
.content { flex: 2 } // twice as large as header
How #ivanS95 says '.container only has a height based on its content'.
Instead, you can do this by setting all parents (html, body) elements to 100% also the .container at 100% too, and changing your flex propierty of .header not allowing it to grow.
Example here:
flex: 0 1;
https://codepen.io/pen/
This question was very nicely answered before by #Pebbl at:
Make a div fill the height of the remaining screen space
please check it.

How can I accomplish this design with flexbox?

I'm trying to accomplish this design by using flexbox:
It's supposed to be a one page website.
.container {
display: flex;
}
.big {
flex: 2;
height: 70vh;
background: gray;
}
.small {
flex: 1;
height: 70vh;
background: gray;
}
<div class="container">
<div class="small">
</div>
<div class="smallest">
</div>
<div class="big">
</div>
</div>
I have no idea how to implement the "smallest" div to be 25% of the big, let alone make the "small" 75% of the big one.
Also the height really confuses me, I need them to always have the same height.
With flexbox you can wrap the small and the smallest into a separate div and use column flexbox on the left section.
I have no idea how to implement the "smallest" div to be 25% of the big
25% to 75% ratio means 1:3 ratio - and in flexbox language that is flex: 1 to the small element and flex: 3 to the big element.
Also the height really confuses me, I need them to always have the same height.
You can set the height of the container to the container element - your flexbox will fill to this height.
See demo below:
.container {
display: flex;
height: 70vh;
}
.big {
flex: 3;
background: gray;
margin-left: 5px;
}
.left {
flex: 1;
display: flex;
flex-direction: column;
}
.left .small {
background: gray;
flex: 3;
}
.left .smallest {
margin-top: 5px;
background: gray;
flex: 1;
}
<div class="container">
<div class="left">
<div class="small">
</div>
<div class="smallest">
</div>
</div>
<div class="big">
</div>
</div>

Scrollbar and its content is hidden outside of div

So I have a problem where I have 2 divs inside of another div with a fixed size. I the second of the two is too large to fit in the fixed height div so I want a scroll bara to appear. But the scrollbar goes outside of the content. How do I fix this?
html:
<div class="main">
<div class="first-child">
<div class="small-content">
Content
</div>
</div>
<div class="second-child">
<div class="large-content">
Content
</div>
</div>
</div>
css:
.main {
height: 250px;
overflow: hidden;
}
.first-child {
background-color: red;
}
.second-child {
max-height: 100%;
background-color: blue;
overflow-y: scroll;
}
.large-content {
padding-top: 300px;
}
.small-content {
padding: 10px;
}
https://codepen.io/RilleJ/pen/JeBVpz
I added an example as well to show what I mean. Basically I want to be able to scroll all the way down in the blue box and see the content without setting a fixed height. (Not that the content above, the red box, can be different sizes)
Use flexbox to divide the space of the container among the children.
Add flex-grow: 0, and flex-shrink: 0 for a child that just needs to take the space it needs for its content.
Add flex-grow: 1, and flex-shrink: 1 on the other children to divide the remaining space equally (each child will take at least the size of its content).
.main {
height: 250px;
overflow: hidden;
display: flex;
flex-direction: column;
}
.first-child {
flex-grow: 0;
flex-shrink: 0;
background-color: red;
}
.second-child {
flex-grow: 1;
flex-shrink: 1;
background-color: blue;
overflow-y: scroll;
}
.large-content {
padding-top: 300px;
}
.small-content {
padding: 10px;
}
<div class="main">
<div class="first-child">
<div class="small-content">
Content
</div>
</div>
<div class="second-child">
<div class="large-content">
Content
</div>
</div>
</div>

Moving one HTML element before another while maintaining document flow

I am trying to create tab module such as this:
https://codepen.io/oknoblich/pen/tfjFl
However I am having difficulty since I can not change the HTML layout:
<div class="container">
<div class="tab-header">Tab1</div>
<div class="tab-content teal">Content1</div>
</div>
<div class="container current">
<div class="tab-header">Tab2</div>
<div class="tab-content teal">Content2</div>
</div>
The problems are that absolute positioning removes the content from the document flow, while other methods prevents the content from being the full width of the page.
I created two codepen's that illustrates the difficulties:
https://codepen.io/dwigt/pen/pOQpLd (absolute positioning removes content from document flow)
https://codepen.io/dwigt/pen/YOREOJ (flexbox layout does not take up full page-width)
Is there anyway I can replicate the tab functionality using this HTML layout and no javascript?
You can use display: contents (which is unfortunately not too well supported) combined with flexbox layout with wrap, set on the .wrapper element. This way, tab-headers and tab-contents will be treated equally, as if they were at the same level with one another - the .container elements are "transparent" to the layout engine. As a result, they will all be laid out with flexbox logic applied. Finally, to have the three tab headers display first, we set the order of the tab contents to some high value (here 100), and since we have flex wrap enabled, the content is then pushed downwards to a new line, below the headers. See example below:
.wrapper {
max-width: 300px;
display: flex;
flex-wrap: wrap;
height: 100%;
}
.container {
height: 50px;
display: contents;
}
.container .tab-header {
width: 100px;
max-width: 100%;
max-height: 100%;
flex: 1 0 33.33%;
}
.container .tab-content {
display: none;
height: 200px;
order: 100;
}
.container.current .tab-content {
display: block;
width: 300px;
left: 0;
}
.footer {
height: 500px;
width: 300px;
display: block;
}
.red {
background: red;
}
.teal {
background: teal;
}
<div class="wrapper">
<div class="container">
<div class="tab-header">Tab1</div>
<div class="tab-content teal">Content1</div>
</div>
<div class="container current">
<div class="tab-header">Tab2</div>
<div class="tab-content teal">Content2</div>
</div>
<div class="container">
<div class="tab-header">Tab3</div>
<div class="tab-content teal">Content3</div>
</div>
</div>
<div class="footer red">Footer Text</div>
This isn't quite perfect because the first tab is a bit wider, but give this a shot and see if this doesn't get your closer to your goal. It allows your tabs to be 100% and also allows you to add more tabs that space evenly from edge to edge of your container.
Let me know how it works out :D
Add display: table-cell and width: 100% to your css selector label
label {
display: table-cell;
width: 100%;
margin: 0 0 -1px;
padding: 15px 25px;
font-weight: 600;
text-align: center;
color: #bbb;
border: 1px solid transparent;
}