Flexbox shrinking main container when main container is horizontal flex box - html

I want to create a simple layout for my SPA where I have a header component, which should be shown all the time. A footer component, which should be either at the bottom of the screen or if the main content component is bigger than the screen, it should be below the content component. And a main content component which should get the rest of the space if the screen is bigger than the content, so the footer component gets rendered at the bottom.
Currently I've reproduced my layout in this codepen. But if you shrink the result window of the codepen enough, you'll get to a point where I can't see the Test2 text because the footer component is on top of it. The behaviour I would expect is, that I can see the Test2 text and I'm able to scroll down to the footer component.
It works if the content component is not a flex box with flex-direction: row. Any ideas why this doesn't work?
In my SPA I'm using React, so I don't want to use any JavaScript for this.
* {
padding: 0;
margin: 0;
font-size: 2rem;
}
.page {
display: flex;
flex-direction: column;
height: 100vh;
width: 100%;
background: yellow;
}
.container {
display: flex;
flex-direction: column;
flex: 1;
overflow: scroll;
}
.main {
flex: 1;
background: red;
display: flex;
flex-direction: row;
min-height: 30px;
}
.test {
display: block;
}
.footer {
background: green;
}
<div class="page">
<div class="header">Header</div>
<div class="container">
<div class="main">
Main
<div class="test">Test1<br />Test2<br/></div>
</div>
<div class="footer">Footer</div>
</div>
</div>

Just add flex: 1 0 auto to .main class. Flex properties are flex-grow, flex-shrink, and the flex-basis. So with 0 it is told to not shrink.
* {
padding: 0;
margin: 0;
font-size: 2rem;
}
.page {
display: flex;
flex-direction: column;
height: 100vh;
width: 100%;
background: yellow;
}
.container {
display: flex;
flex-direction: column;
flex: 1;
overflow: scroll;
}
.main {
flex: 1 0 auto;
background: red;
display: flex;
flex-direction: row;
min-height: 30px;
}
.test {
display: block;
}
.footer {
background: green;
}
<div class="page">
<div class="header">Header</div>
<div class="container">
<div class="main">
Main
<div class="test">Test1<br />Test2<br/></div>
</div>
<div class="footer">Footer</div>
</div>
</div>

I got it working. I needed to add a div with display: block; around my content and I added justify-content: space-between; to my container component. Little hacky but it works...
That's my final code:
* {
padding: 0;
margin: 0;
font-size: 2rem;
}
.page {
display: flex;
flex-direction: column;
height: 100vh;
width: 100%;
background: yellow;
}
.container {
display: flex;
flex-direction: column;
flex: 1;
justify-content: space-between;
overflow: scroll;
}
.block {
display: block;
}
.main {
background: red;
display: flex;
flex-direction: row;
}
.test {
display: block;
}
.footer {
background: green;
}
<div class="page">
<div class="header">Header</div>
<div class="container">
<div class="block">
<div class="main">
Main
<div class="test">Test1<br />Test2<br/></div>
</div>
</div>
<div class="footer">Footer</div>
</div>
</div>

Related

How to fix child element not getting height of its parent element?

I have a page like this:
* {
padding: 0;
margin: 0;
}
.pageWrapper {
display: flex;
flex-direction: column;
min-height: 100vh;
}
.header {
background-color: saddlebrown;
height: 40px;
}
.main {
flex: 1;
position: relative;
}
.innerWrapper {
height: 100%;
background-color: red;
display: flex;
justify-content: center;
align-items: center;
}
.footer {
height: 40px;
background-color: blueviolet;
}
<div class="pageWrapper">
<header class="header"></header>
<main class="main">
<div class="innerWrapper">
<button>Button</button>
</div>
</main>
<footer class="footer"></footer>
</div>
As I understand, innerWrapper is not getting 100% height of main because main's height is not defined explicitly, it's flex:1. Is it correct? How do I make innerWrapper get the height of main in this particular case?
Just remove the flex and the heigh properties from the children and add align-items: stretch; and display: flex; to the parent.
* {
padding: 0;
margin: 0;
}
.pageWrapper {
display: flex;
flex-direction: column;
min-height: 100vh;
}
.header {
background-color: saddlebrown;
height: 40px;
}
.main {
display: flex;
flex: 1;
align-items: stretch;
}
.innerWrapper {
background-color: red;
width: 100%;
justify-content: center;
align-items: center;
}
.footer {
height: 40px;
background-color: blueviolet;
}
<div class="pageWrapper">
<header class="header"></header>
<main class="main">
<div class="innerWrapper">
<button>Button</button>
</div>
</main>
<footer class="footer"></footer>
</div>
Your .main is occupying its container's height/width. It is your .innerWrapper that is not occupying the height of its container.
The reason for this is that the height of .main is not explicit. According to this:
If the height of the containing block is not specified explicitly
(i.e., it depends on content height), and this element is not
absolutely positioned, the value computes to 'auto'
You can change .main to be flex also and change .innerWrapper to have flex: 1:
.main {
flex: 1;
position: relative;
background-color: orange; // Added for visual only. Remove from your code.
display: flex;
flex-direction: column;
}
.innerWrapper {
flex: 1;
height: 100%;
background-color: red;
display: flex;
justify-content: center;
align-items: center;
}
See it work here.
I think this is the thing you are trying to achieve...
Check the code. I have added comments to identify the code which is added...
* {
padding: 0;
margin: 0;
}
.pageWrapper {
display: flex;
flex-direction: column;
min-height: 100vh;
}
.header {
background-color: saddlebrown;
height: 40px;
}
.main {
flex: 1;
position: relative;
background-color:blue;
}
.innerWrapper {
height: 100%;
background-color: red;
display: flex;
justify-content: center;
align-items: center;
/*code added here*/
position:absolute;
top:0;
bottom:0;
/* if you want to add button to center add this property here right:50%;*/
}
.footer {
height: 40px;
background-color: blueviolet;
}
<div class="pageWrapper">
<header class="header"></header>
<main class="main">
<div class="innerWrapper">
<button>Button</button>
</div>
</main>
<footer class="footer"></footer>
</div>
If you make the display value block instead of flex at the class .innerWrapper
* {
padding: 0;
margin: 0;
}
.pageWrapper {
display: flex;
flex-flow: column wrap;
min-height: 100vh;
height: 100vh;
}
.header {
background-color: saddlebrown;
height: 40px;
}
.main {
flex: 1 1 auto;
position: relative;
}
.innerWrapper {
height:100%;
background-color: red;
display: block;
text-align:center;
}
.footer {
height: 40px;
background-color: blueviolet;
}
<div class="pageWrapper">
<header class="header"></header>
<main class="main">
<div class="innerWrapper">
<button>Button</button
</div>
</main>
<footer class="footer"></footer>
</div>

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>

Height 100% inside flex item

I have a layout that is mainly divided into 3 parts and the middle one should take a full height. And it does.
However, I need an additional div which will play a role of the backdrop and here the problem comes. The child doesn't want to take 100% height.
Here .body is a div that is being stretched when there is not enough content and .bg-gray is the one I want to take its parent full height.
Is there a way achieve this without using relative + absolute positioning?
Also, I'm looking for the answer to my question: why is this happening that way.
html, body {
padding: 0;
margin: 0;
}
.container {
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: stretch;
}
.header {
height: 50px;
background-color: #e6e6e6;
}
.footer {
height: 50px;
background-color: #aaa444;
}
.body {
flex: 1;
}
.bg-gray {
background-color: #eee;
min-height: 100%;
flex: 1;
}
<div class="container">
<div class="header">
</div>
<div class="body">
<div class="bg-gray">
<div>
asdasd
</div>
</div>
</div>
<div class="footer">
</div>
</div>
Apply flexbox to the .body div.
.body {
flex: 1;
display: flex;
flex-direction: column;
}
html,
body {
padding: 0;
margin: 0;
}
.container {
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: stretch;
}
.header {
height: 50px;
background-color: #e6e6e6;
}
.footer {
height: 50px;
background-color: #aaa444;
}
.body {
flex: 1;
display: flex;
flex-direction: column;
}
.bg-gray {
background-color: darkgrey;
min-height: 100%;
flex: 1;
}
.bg-gray div {
background: lightblue;
}
<div class="container">
<div class="header">
</div>
<div class="body">
<div class="bg-gray">
<div>
asdasd
</div>
</div>
</div>
<div class="footer">
</div>
</div>

How to stretch div to full height with flex

How do I stretch the divs with a yellow background to full height? It should cover up the green but it is not working. I tried adding height: 100% on it but then it adds up the height from the search bar?
https://jsfiddle.net/nuy20j1h/
.block {
width: 80%;
margin: 0 auto;
display: flex;
flex-wrap: wrap;
}
.sidebar {
height: 600px;
width: 25%;
background: red;
}
.home {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
width: 75%;
background: green;
}
.search-bar {
width: 100%;
padding: 25px;
background: blue;
}
.content-wrap {
display: flex;
flex-wrap: wrap;
width: 100%;
align-items: flex-stretch;
}
.content,
.single {
width: 50%;
background: yellow;
}
<div class="block">
<div class="sidebar">sidebar</div>
<div class="home">
<div class="search-bar">search bar</div>
<div class="content-wrap">
<div class="content">lorem ipsum</div>
<div class="single">test</div>
</div>
</div>
</div>
First you should add a style reset, I'm using this now * {} as you can se below. The trick here is to run flex-direction: column; on .home and you can tell .content-wrap to take up the rest of that space after the search with flex-grow: 1;
box-sizing: border-box; is, if you add let's say width: 200px; to a element, and add padding: 20px;, the element will stay 200px with the padding included. If you don't have that, it will take up 200px + 40px.
if you want the fiddle, here it is
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
.block {
width: 80%;
margin: 0 auto;
display: flex;
flex-wrap: wrap;
}
.sidebar {
height: 600px;
width: 25%;
background: red;
}
.home {
display: flex;
flex-direction: column;
flex-wrap: wrap;
align-items: flex-start;
width: 75%;
background: green;
}
.search-bar {
width: 100%;
padding: 25px;
background: blue;
}
.content-wrap {
display: flex;
flex-grow: 1;
flex-wrap: wrap;
width: 100%;
align-items: flex-stretch;
}
.content,
.single {
width: 50%;
background: yellow;
}
<div class="block">
<div class="sidebar">sidebar</div>
<div class="home">
<div class="search-bar">search bar</div>
<div class="content-wrap">
<div class="content">lorem ipsum</div>
<div class="single">test</div>
</div>
</div>
</div>
As mentioned in other answers, there is one main issue here:
flex-direction: column;, which I added to home, to enable the usage of flex properties instead of height, to make the .content-wrap fill the available space left in home
That will make the .search-bar and .content-wrap stack vertical, and enable the use of flex: 1 on .content-wrap, which will make it fill the remaining space/height.
So even if you got answers already, and since there are some properties with wrong value, or not needed, I decided to post an answer to clarify the changes made.
See my notes made in the CSS for further clarifications and what I changed.
Stack snippet
.block {
width: 80%;
margin: 0 auto;
display: flex;
flex-wrap: wrap;
}
.sidebar {
height: 600px;
width: 25%;
background: red;
}
.home {
display: flex;
flex-direction: column; /* added */
/*flex-wrap: wrap; removed, not needed */
/*align-items: flex-start; removed, items should fill parent's,
in this changed case, width */
width: 75%;
background: green;
}
.search-bar {
/*width: 100%; not needed, default for column
item is to fill parent width as
its "align-items" is "stretch" */
padding: 25px;
background: blue;
}
.content-wrap {
flex: 1; /* added, take the remaining space left
left of its parent (height in this case) */
display: flex;
flex-wrap: wrap;
/*width: 100%; not needed, default for column
item is to fill parent width as
its "align-items" is "stretch" */
/*align-items: flex-stretch; wrong value, should be "stretch",
though since that is the default,
it is not needed */
}
.content,
.single {
width: 50%;
background: yellow;
}
<div class="block">
<div class="sidebar">sidebar</div>
<div class="home">
<div class="search-bar">search bar</div>
<div class="content-wrap">
<div class="content">lorem ipsum</div>
<div class="single">test</div>
</div>
</div>
</div>
flex-direction: column; is your friend. Here is a reworked fiddle of your code: https://jsfiddle.net/vsjktmms/1/
Using the same HTML structure you provided:
.block {
display: flex;
width: 80%;
margin: 0 auto;
background-color: gray;
align-items: stretch;
}
.sidebar {
width: 25%;
height: 600px;
background-color: red;
}
.home {
display: flex;
flex-direction: column;
align-items: stretch;
width: 75%;
background-color: green;
}
.search-bar {
padding: 25px;
background-color: blue;
}
.content-wrap {
flex: 1 1 auto;
display: flex;
flex-wrap: wrap;
width: 100%;
background-color: pink;
}
.content,
.single {
width: 50%;
background: yellow;
}

Vertical align of flexbox childs's child

I have a flexbox container with header, footer and a content. Using flexbox is no problem for me to stretch content so that it takes all available vertical space (colored green).
But I cannot center vertically all that is inside content after. I was first thinking to do the same flexbox approach, but it fails. In my example, basically, the red area must take all space that green area takes, and the text should be centered vertically and horizontally inside green area.
What do I miss?
You can also add display: flex on main and remove flex-grow: 1 from item.
html,
body {
height: 100%;
background-color: #ddd;
}
body {
display: flex;
flex-direction: column;
}
header,
footer {
background-color: #abc
}
main {
flex: 1;
background-color: #bada55;
display: flex;
}
.wrapper {
display: flex;
flex: 1;
justify-content: center;
align-items: center;
background-color: red;
}
p {
margin: 0;
}
<header>HEADER</header>
<main>
<div class="wrapper">
<div class="item">
<p>This should be centered both VERTICALLY & HORIZONTALLY.</p>
</div>
</div>
</main>
<footer>FOOTER</footer>
Added
main {
display: flex;
align-items: center;
background-color: red;
}
html,
body {
height: 100%;
background-color: #ddd;
}
body {
display: flex;
flex-direction: column;
}
header,
footer {
background-color: #abc
}
main {
flex: 1;
background-color: #bada55;
position: relative;
display: flex;
align-items: center;
background-color: red;
}
.wrapper {
width: 100%;
text-align: center;
}
.item {
flex-grow: 1;
}
p {
margin: 0;
}
<header>HEADER</header>
<main>
<div class="wrapper">
<div class="item">
<p>This should be centered both VERTICALLY & HORIZONTALLY.</p>
</div>
</div>
</main>
<footer>FOOTER</footer>