Absolute Position Vertical Nav with Flexbox - html

I want to make a vertical nav that say will be 50px and then I want to have a flex area that has my header, main content area and footer.
Right now when I use absolute the flexbox container gets covered over as absolute it doing it's own thing. I am wondering if I can tell my flex container to start 50px from the left so I don't have to worry about icons and such getting swallowed up by it.
Do I have to make the flex container absolute as well?

You don't need any positioning or margins, just make it natural with the additional flex wrapper:
body {margin: 0}
.outerFlex {
display: flex; /* displays flex-items (children) inline */
height: 100vh; /* 100% of the viewport height */
}
nav {
flex-basis: 50px; /* initial width */
background: lightblue;
}
.innerFlex {
flex: 1; /* takes the remaining width */
display: flex;
flex-direction: column; /* stacks flex-items vertically */
background: lightgreen;
}
main {
flex: 1; /* takes the remaining height */
}
<div class="outerFlex">
<nav>Nav</nav>
<div class="innerFlex">
<header>Header</header>
<main>Main</main>
<footer>Footer</footer>
</div>
</div>

You can use margin-left:50px on the flex area to make it start at 50px;
see code sample
body {
margin: 0;
height: 100vh;
}
.container {
height: 100%;
}
.nav {
position: absolute;
width: 50px;
background: green;
height: 100%;
}
.flex-container {
display: flex;
background: yellow;
height: 100%;
margin-left: 50px;
}
<div class="container">
<div class="nav"></div>
<div class="flex-container">text sample</div>
</div>

You just need to position your nav as absolute or fixed, then give padding/margin that equals the width of your nav to the main content.
Here's an example.
.container {
width: 100%;
}
.left-nav {
position: fixed;
width: 50px;
height: 100%;
background: black;
}
.main {
display: flex;
flex-direction: column;
justify-content: space-between;
padding-left: 50px;
background: rgba(255,0,0,0.1);
}
.main-header {
background: red;
}
.main-body {
background: green;
}
.main-footer {
background: blue;
}
<div class="container">
<div class="left-nav">Nav</div>
<div class="main">
<div class="main-header">Header</div>
<div class="main-body">Body</div>
<div class="main-footer">Footer</div>
</div>
</div>

Related

How can I make a div grow to fit its content, up to the max size of the parent container?

I'm positioning a modal div in the middle of the page with a max-height defined as a % of its container, in this case it shouldn't exceed 70% of the page height.
The contents of the div are two elements:
A fixed height header div
And a variable height content div
I want the modal to grow with the content until the max-height, then the content div should start scrolling. However whatever I do, the content seems to spill out of the modal like so:
This is my markup and styling:
body {
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.modal {
max-height: 70%;
border: 5px dashed green;
border-radius: 5px;
}
.container {
/* I cannot remove this container */
display: flex;
flex-direction: column;
width: 600px;
}
.header {
flex: none;
background: red;
height: 100px;
}
.content {
flex: 1;
background: yellow;
overflow: auto;
}
.big-stuff {
margin: 10px;
background: orange;
height: 600px;
}
<div class="modal">
<div class="container">
<div class="header">
Header
</div>
<div class="content">
Content. Should shrink or grow to fit content but only to a max of the container height
<div class="big-stuff">
Large content
</div>
</div>
</div>
</div>
Unfortunately I can't change the markup so I'm trying to get this working just by modifying CSS. If I remove the .container div, then everything seems to work but I was hoping there was another way.
Full example available here: https://codepen.io/dyancat/pen/QWaOGpB
You can add flex to the modal, so that the content will not expand out of its parent (modal in this example):
.modal {
max-height: 70%;
border: 5px dashed green;
border-radius: 5px;
display: flex; /* Add this flex */
}
Just add display:flex in the css to the modal class.
body {
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.modal {
max-height: 70%;
border: 5px dashed green;
border-radius: 5px;
display: flex;
}
.container {
/* I cannot remove this container */
display: flex;
flex-direction: column;
width: 600px;
}
.header {
flex: none;
background: red;
height: 100px;
}
.content {
flex: 1;
background: yellow;
overflow: auto;
}
.big-stuff {
margin: 10px;
background: orange;
height: 600px;
}
<div class="modal">
<div class="container">
<div class="header">
Header
</div>
<div class="content">
Content. Should shrink or grow to fit content but only to a max of the container height
<div class="big-stuff">
Large content
</div>
</div>
</div>
</div>

Content overflowing flex item despite overflow property

I have a pretty simple page setup in the following manner using flexboxes:
The blue div is supposed to make up 25% in height and the violet div 75%. In case there are too many lines in the blue div, it should stay the same size an show a scrollbar. This works for a few lines, but breaks at some point and the blue div overflows and grows into the violet one. I'm new to flexboxes, so I don't really understand why this is happening. Would I be better off not using flexboxes? Thankful for any hints or pointer at this point.
This is the code I use (run in full page):
function lines(noLines) {
var text = "line</br>".repeat(noLines);
document.getElementById("lower").innerHTML = text;
}
* {
box-sizing: border-box;
}
.body {
display: flex;
flex-direction: column;
min-height: 100vh;
}
#static1 {
height: 30px;
width: 100%;
background-color: red;
}
#static2 {
height: 40px;
width: 100%;
background-color: orange;
}
#content {
display: flex;
flex: 1;
}
#left {
width: 40%;
background-color: yellow;
}
#right {
width: 60%;
display: flex;
flex-direction: column;
}
#upper {
flex: 3 0;
background-color: violet;
}
#lower {
flex: 1;
background-color: blue;
overflow: auto;
}
<div class="body">
<div id="static1">Some static div</div>
<div id="static2">Another static div. Flexbox below fills rest of remaining screen.</div>
<div id="content">
<div id="left">
Left part, fixed width in percentage.</br>
Click to enter lines into the bottom right:</br>
<button onclick=lines(20)>Few Lines</button>
<button onclick=lines(200)>Many Lines</button>
</div>
<div id="right">
<div id="upper">Flexbox with flex=3.</div>
<div id="lower">Flexbox with flex=1.</div>
</div>
</div>
</div>
For the overflow property to work properly, the container needs an actual height or max-height. Flex heights (you have flex: 1 on .content) won't cut it.
In order for overflow to have an effect, the block-level container
must have either a set height (height or max-height) or
white-space set to nowrap. ~ MDN
Since you already know the height of the primary container (100vh) and the first two rows (30px and 40px), the rest is simple using the calc() function.
function lines(noLines) {
var text = "line</br>".repeat(noLines);
document.getElementById("lower").innerHTML = text;
}
.body {
display: flex;
flex-direction: column;
height: 100vh; /* adjustment */
}
#static1 {
flex-shrink: 0; /* disable shrinking */
height: 30px;
/* width: 100%; */
background-color: red;
}
#static2 {
flex-shrink: 0; /* disable shrinking */
height: 40px;
/* width: 100%; */
background-color: orange;
}
#content {
height: calc(100vh - 70px); /* new */
display: flex;
/* flex: 1; */ /* may work in some browsers, but not reliable */
}
#left {
width: 40%;
background-color: yellow;
}
#right {
width: 60%;
display: flex;
flex-direction: column;
}
#upper {
flex: 3 0;
background-color: violet;
}
#lower {
flex: 1;
background-color: aqua; /* adjusted for illustration */
overflow: auto;
}
body {
margin: 0; /* new; override browser default */
}
* {
box-sizing: border-box;
}
<div class="body">
<div id="static1">Some static div</div>
<div id="static2">Another static div. Flexbox below fills rest of remaining screen.</div>
<div id="content">
<div id="left">
Left part, fixed width in percentage.<br> Click to enter lines into the bottom right:<br>
<button onclick=lines(20)>Few Lines</button>
<button onclick=lines(200)>Many Lines</button>
</div>
<div id="right">
<div id="upper">Flexbox with flex=3.</div>
<div id="lower">Flexbox with flex=1.</div>
</div>
</div>
</div>
jsFiddle demo
I hope this is what you mean, but If I'm wrong, apologies. The problem I can see lies in the way you are using flex: 1 & flex: 3 to define the proportions of the right column, without specifying to what height their parent container has, i.e. #right has no height, so the box can always expand as it gets more filled with content.
Please try this, I hope this works and if I can answer anything else, just ask please.
The only thing I changed was your CSS and added max-height: calc(100vh - 70px); to the #right div. And changed overflow: auto; to overflow-y: scroll;
* {
box-sizing: border-box;
}
.body {
display: flex;
flex-direction: column;
min-height: 100vh;
}
#static1 {
height: 30px;
width: 100%;
background-color: red;
}
#static2 {
height: 40px;
width: 100%;
background-color: orange;
}
#content {
display: flex;
flex: 1;
}
#left {
width: 40%;
background-color: yellow;
}
#right {
width: 60%;
display: flex;
flex-direction: column;
max-height: calc(100vh - 70px);
}
#upper {
flex: 3;
height: 75%;
background-color: violet;
}
#lower {
flex: 1;
height: 25%;
background-color: blue;
overflow-y: scroll;
}
Change the top part of CSS to this:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}

Flex item content overflows container

I have setup the following layout. The content__item elements (which are commented below) are overflowing vertically outside main container.
.root {
display: flex;
height: 100vh;
background-color: gray;
}
.nav {
width: 16rem;
background-color: red;
}
.main {
flex-grow: 1;
background-color: green;
}
.menu {
height: 4rem;
background-color: blue;
}
.content {
display: flex;
height: 100%;
padding: 2rem;
}
.content__item {
flex: 1;
margin-left: 1rem;
background-color: white;
}
<div className="root">
<nav className="nav"></nav>
<main className="main">
<div className="menu"></div>
<div className="content">
<!-- Overflowing -->
<div className="content__item"></div>
<div className="content__item"></div>
<div className="content__item"></div>
</div>
</main>
</div>
I am pretty sure its a flexbox bug. I tried using min-height: 0 on the container but it still doesn't work. I setup an environment here for reference.
The reason the content_items are overflowing is because height: 100% causes problems with flex. However if you remove that, the elements don't appear to fill the available height. This is because their parent (the content div) is not the child of a flex element, so it is in fact this element and not the content__item that isn't taking up the available height.
We can fix this by adding display:flex to the main div (the parent of content)... however now we have another problem! This makes the other child of content (the nav element) appears to the side. To fix this, we can use flex-direction: column;
The main changes you need to make this work as as follows:
.main {
flex-grow: 1; /* you already have this to allow the children grow */
display: flex; /* Add this so the content element can use the full height */
flex-direction: column; /* Add this to make the children stack one below another */
}
.content {
display: flex; /* you already had this */
flex:1; /* Add this to make it take up the available height */
}
.content__item {
flex: 1; /* You don't actually need this now */
}
Working Example:
Also FYI, you need to set the body margin to 0 - otherwise the 100vh extends larger than the screen as it is getting added to the default margin.
body { margin:0; }
.root {
display: flex;
height: 100vh;
background-color: gray;
}
.nav {
width: 16rem;
background-color: red;
}
.main {
flex-grow: 1;
background-color: green;
display:flex;
flex-direction:column;
}
.menu {
height: 4rem;
background-color: blue;
}
.content {
display: flex;
padding: 2rem;
flex:1;
}
.content__item {
margin-left: 1rem;
background-color: white;
}
<div class="root">
<nav class="nav"></nav>
<main class="main">
<div class="menu"></div>
<div class="content">
<!-- Overflowing -->
<div class="content__item">some text here</div>
<div class="content__item">some text here</div>
<div class="content__item">some text here</div>
</div>
</main>
</div>
body, html {
margin: 0;
}
.root {
display: flex;
height: 100vh;
background-color: gray;
}
.nav {
width: 16rem;
background-color: red;
}
.main {
flex-grow: 1;
background-color: green;
display: flex;
flex-direction: column;
}
.menu {
height: 4rem;
background-color: blue;
}
.content {
display: flex;
padding: 2rem;
flex: 1;
}
.content__item {
flex: 1;
margin-left: 1rem;
background-color: white;
}
<div class="root">
<nav class="nav"></nav>
<main class="main">
<div class="menu"></div>
<div class="content">
<!-- Overflowing -->
<div class="content__item">a</div>
<div class="content__item">b</div>
<div class="content__item">c</div>
</div>
</main>
</div>

How to do a sticky footer and still be able to do scrollable flexbox content?

I'm trying to achieve the sticky footer (flexbox version). However, I'm unable to find a working solution if I also want the ability to have scrollable content inside a flex: 1 div (which requires parents to have height: 100%).
Here's a fiddle to demonstrate the problem:
https://jsfiddle.net/gfaqLh42/6/
As you can see, the red area is scrollable (with a min-height: 300px). Notice the footer is offscreen even though the viewport is not less than the red area's min-height + blue area.
Is there a way to do a sticky footer and still use flexbox flex: 1 with scrollable content?
Update
Here's another picture to represent the other big problem I face in trying to make this work:
Is there a way to do a sticky footer and still use flexbox flex: 1
with scrollable content?
Yes, and what you need is to use Flexbox all the way.
So instead of using min-height/height on article-1/card, change their CSS to this:
.article-1 {
flex: 1;
display: flex;
min-height: 0; /* added, i.a Firefox need this */
}
.card {
overflow: auto;
}
Note, I also remove some properties not needed, mainly as they were set to their defaults, and added some. And why the need of min-width, is well explained here:
Why don't flex items shrink past content size?
Updated fiddle
Stack snippet
html, body{
height: 100%;
margin: 0;
font-weight: bold;
}
.header {
position: absolute;
height: 40px;
background-color: grey;
z-index: 1;
width: 100%;
}
.content {
display: flex;
flex-direction: column;
height: 100%;
padding-top: 40px;
box-sizing: border-box; /* added */
}
.wrap {
flex: 1;
display: flex;
flex-direction: column;
min-height: 0; /* added, i.a Firefox need this */
}
.container {
flex: 1;
padding: 10px;
box-sizing: border-box; /* added */
display: flex;
flex-direction: column;
min-height: 0; /* added, i.a Firefox need this */
}
.article-1 {
flex: 1;
display: flex;
min-height: 0; /* added, i.a Firefox need this */
}
.card {
overflow: auto;
}
.card-text {
height: 2000px;
width: 2000px;
background-color: red;
}
.article-2 {
flex: none;
height: 40px;
background-color: blue;
}
.footer {
position: relative;
height: 40px;
background-color: grey;
}
<div class="header">Header</div>
<div class="content">
<div class="wrap">
<div class="container">
<div class="article-1">
<div class="card">
<div class="card-text">
scrollable flex: 1 div<br>
1. scrollable<br>
2. scrollable<br>
3. scrollable<br>
4. etc...
</div>
</div>
</div>
<div class="article-2">
flex: none div
</div>
</div>
</div>
<div class="footer">Footer</div>
</div>
Updated based on a comment
If there is a need for the article-1 to have a minimum height, and to avoid absolute positioning on it, a minimum height could be set on content as well, to push the footer further down on smaller screens.
Updated fiddle 2
Stack snippet
html, body{
height: 100%;
margin: 0;
font-weight: bold;
}
.header {
position: absolute;
height: 40px;
background-color: grey;
z-index: 1;
width: 100%;
}
.content {
display: flex;
flex-direction: column;
height: 100%;
min-height: 450px; /* added */
padding-top: 40px;
box-sizing: border-box; /* added */
}
.wrap {
flex: 1;
display: flex;
flex-direction: column;
min-height: 0; /* i.a Firefox need this */
}
.container {
flex: 1;
padding: 10px;
box-sizing: border-box; /* added */
display: flex;
flex-direction: column;
min-height: 0; /* i.a Firefox need this */
}
.article-1 {
flex: 1;
display: flex;
min-height: 300px; /* changed */
}
.card {
overflow: auto;
}
.card-text {
height: 2000px;
width: 2000px;
background-color: red;
}
.article-2 {
flex: none;
height: 40px;
background-color: blue;
}
.footer {
position: relative;
height: 40px;
background-color: grey;
}
<div class="header">Header</div>
<div class="content">
<div class="wrap">
<div class="container">
<div class="article-1">
<div class="card">
<div class="card-text">
scrollable flex: 1 div<br>
1. scrollable<br>
2. scrollable<br>
3. scrollable<br>
4. etc...
</div>
</div>
</div>
<div class="article-2">
flex: none div
</div>
</div>
</div>
<div class="footer">Footer</div>
</div>

Element styled with CSS Flex box appears outside parent element and doesn't vertically align with parent

I have a <section> element with a title, that contains a <div> which holds some text. I need the <div> to appear in the middle of the <section> tag, and the <section> should take up the rest of the space under the header. To the user, the <div> should appear in the centre of the space under the header.
My following code does that to some degree, but it appears off-centre. I think thats's because I applied height: 100vh to the <section>, which makes that element longer than the rest of the page.
How do I achieve this? I'm trying to create a generic set of styles for the div.message so that I can drop it in when needed and it will appear in the centre of the area below the header.
header {}
.content {
height: 100vh;
}
.message {
height: 100%;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: center;
align-content: stretch;
align-items: center;
}
.message .text {
font-size: 20px;
order: 0;
flex: 0 1 auto;
align-self: auto;
}
<header>
<h1>Header area</h1>
</header>
<section class="content">
<h2>This is a section</h2>
<div class="message">
<p class="text">This section is empty</p>
</div>
</section>
JSFiddle
Here is how I recommend you do, and get a good responsive layout:
Add a wrapper, the container (could also use the body)
Make the container a flex column container so the header and content will stack vertically
Set flex-grow: 1 on content so if take the remaining space of its parent
Make the content a flex column container
Set flex-grow: 1 on message so if take the remaining space of its parent
Make the message a flex row container (the default)
Set justify-content: center; align-items: center; on message so its content centers
Finally, we need to take the h2 out of flow or else the message won't fill its entire parent's height, and if not, the message won't center vertically in the section
Note, as the h2 is positioned absolute the content could also be set as a flex row container, though I choose to use "column" to make it move obvious compared with the markup structure
Updated fiddle
Stack snippet
html, body {
margin: 0;
}
.container {
display: flex;
flex-direction: column;
height: 100vh;
}
header {}
.content {
position: relative;
flex-grow: 1;
display: flex;
flex-direction: column;
}
.content h2 {
position: absolute;
top: 0;
left: 0;
width: 100%;
text-align: center;
}
.message {
flex-grow: 1;
display: flex;
justify-content: center;
align-items: center;
}
.message .text {
font-size: 20px;
}
/* styles for this demo */
html, body {
margin: 0;
}
.container {
}
header {
border: 1px dotted red;
}
.content {
border: 1px dotted red;
}
.message,
.message .text {
border: 1px dotted red;
}
<div class="container">
<header>
<h1>Header area</h1>
</header>
<section class="content">
<h2>This is a section</h2>
<div class="message">
<p class="text">This section is empty</p>
</div>
</section>
</div>
Based on how you intend to use message, you could also set the justify-content: center; align-items: center; to the content (and drop the flex properties on the message)
Fiddle demo 2
Stack snippet
html, body {
margin: 0;
}
.container {
display: flex;
flex-direction: column;
height: 100vh;
}
header {}
.content {
position: relative;
flex-grow: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.content h2 {
position: absolute;
top: 0;
left: 0;
width: 100%;
text-align: center;
}
.message {
}
.message .text {
font-size: 20px;
}
/* styles for this demo */
html, body {
margin: 0;
}
.container {
}
header {
border: 1px dotted red;
}
.content {
border: 1px dotted red;
}
.message,
.text {
border: 1px dotted red;
}
<div class="container">
<header>
<h1>Header area</h1>
</header>
<section class="content">
<h2>This is a section</h2>
<div class="message">
<p class="text">This section is empty</p>
</div>
</section>
</div>
If the message is only a wrapper for the p, you could drop it all together.
Fiddle demo 3
If I understood you well, this is what you're looking for :
header {
}
.content {
align-content: center;
align-items: center;
top: 50%;
left: 50%;
height: 100%;
width: 100%;
}
.message {
display: inline-block;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 200px;
height: 100px;
margin: auto;
}
.message .text {
font-size: 20px;
order: 0;
flex: 0 1 auto;
align-self: auto;
}
The JSFiddle link
You have two main ways of solving this:
1) If you assign a fixed height to the header, you can then give the section the remaining height with calc:
.header {
height: 50px;
}
.content {
height: calc(100vh - 50px);
}
You will need to make sure it works even with smaller windows (you might need to add some media queries)
2) If you instead don't want to assign a fixed height to the header, you can wrap header and section into a common parent that is using a flexbox, and allow the section to grow. I wrote this solution here: https://jsfiddle.net/annc8w4j/1/