Flexbox: scrollable content with sticky footer - html

I want to make a box (flex-item in this case) which always stays in the middle of it's container. In that box, there is a header, footer and content section. If the size of the content grows too big in height, I want the content section to be scrollable. The header and footer should always be visible and the box should always stay in it's container.
Here is what I have been able to write:
HTML
<div class="flex-container">
<div class="flex-item">
<header>Header</header>
<div class="content">
A
<br>B
<br>C
<br>D
<br>E
<br>F
<br>G
<br>H
<br>I
<br>J
<br>K
</div>
<footer>Footer</footer>
</div>
</div>
CSS
body {
margin: 120px;
}
.flex-container {
display: flex;
width: 200px;
height: 200px; /* We can assume that the container's height is hardcoded in this example, but the end solution should work even if this value is changed*/
border: 1px solid black;
justify-content: center;
}
.flex-item {
box-sizing: border-box;
width: 150px;
border: 5px solid blue;
align-self: center;
background-color: red;
display: flex;
flex-flow: column;
max-height: 100%;
}
.content {
/* It should be possible to scroll this element when it get too big in height*/
background-color: grey;
flex: 1;
}
The code is hosted on JSFiddle: https://jsfiddle.net/9fduhpev/3/
To explain the same thing visually, here is the current situation:
Here is what I want:

Use overflow-y: auto;.
Read this: http://www.w3schools.com/cssref/css3_pr_overflow-y.asp
body {
margin: 120px;
}
.flex-container {
display: flex;
width: 200px;
height: 200px;
border: 1px solid black;
justify-content: center;
}
.flex-item {
box-sizing: border-box;
width: 150px;
border: 5px solid blue;
align-self: center;
background-color: red;
display: flex;
flex-flow: column;
max-height: 100%;
}
.content {
background-color: grey;
flex: 1;
overflow-y: auto;
}
<div class="flex-container">
<div class="flex-item">
<header>Header</header>
<div class="content">
A
<br>B
<br>C
<br>D
<br>E
<br>F
<br>G
<br>H
<br>I
<br>J
<br>K
<br>L
</div>
<footer>Footer</footer>
</div>
</div>

I suggest you give it overflow: auto. With that it will be scrollable when needed.
body {
margin: 20px;
}
.flex-container {
display: flex;
width: 200px;
height: 200px;
border: 1px solid black;
justify-content: center;
}
.flex-item {
box-sizing: border-box;
width: 150px;
border: 5px solid blue;
align-self: center;
background-color: red;
display: flex;
flex-flow: column;
max-height: 100%;
}
.content {
background-color: grey;
flex: 1;
overflow: auto;
}
<div class="flex-container">
<div class="flex-item">
<header>Header</header>
<div class="content">
A
<br>B
<br>C
<br>D
<br>E
<br>F
<br>G
<br>H
<br>I
<br>J
<br>K
<br>L
</div>
<footer>Footer</footer>
</div>
</div>

I would do something like this:
.content {
height: 100%;
overflow:auto;
background-color: grey;
flex: 1;
}
https://jsfiddle.net/9fduhpev/4/

Related

How do I divide blocks in my page with flexbox?

I'm facing this problem where I want to have a header, sidebar and content with flexbox. I can't get to a solution to divide these 3 childs. I've been trying to use flex-grow and flex-direction:row but I'm having a problem.
Image of the website
How I want it to be
<style>
.parent {
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
height: 100vh;
border: 20px solid black;
display: flex;
flex-direction: column;
}
.header {
width: 200px;
height: 200px;
background: cornflowerblue;
}
.side {
width: 200px;
height: 200px;
background: rgb(219, 133, 133);
}
.content {
width: 200px;
height: 200px;
background: rgb(115, 202, 180);
}
.text {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
height: 190px;
color: #fff;
}
</style>
<div class="parent">
<div class="header">
<h2 class="text">Header</h2>
</div>
<div class="side">
<h2 class="text">Side</h2>
</div>
<div class="content">
<h2 class="text">Content</h2>
</div>
</div>
You need to create two containers, one for all your elements and one for your header and content.
<div class="parent"> <!-- Container 1 -->
<div class="side">
<h2 class="text">Side</h2>
</div>
<div class="container"> <!-- Container 2 -->
<div class="header">
<h2 class="text">Header</h2>
</div>
<div class="content">
<h2 class="text">Content</h2>
</div>
</div>
</div>
Then, you can treat each container as a separate flex-box. the parent will have a flex-direction: row; and the container will have flex-direction: column;.
You also want to set values in percentages, not absolute values as you have right now (200px, 20rem..).
.parent {
box-sizing: border-box;
margin: 0;
padding: 0;
height: 100vh;
border: 20px solid black;
display: flex;
flex-direction: row;
}
.header {
height: 30%;
background: cornflowerblue;
}
.side {
width: 30%;
height: 100%;
background: rgb(219, 133, 133);
}
.content {
height: 70%;
background: rgb(115, 202, 180);
}
.text {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
height: 190px;
color: #fff;
}
.container {
display: flex;
flex-direction: column;
width: 70%;
height: 100%;
}
JSFiddle
Images to illustrate the separation:
You have to wrap your header & content section inside another div. Something like this below example. However, The best way to achieve this layout is using a CSS grid. Here is the complete CSS grid guide
.parent {
display: flex;
height: 100vh;
background: #000;
padding: 5px;
margin: 0;
}
.side {
border: 1px solid #000;
width: 30vw;
display: flex;
justify-content: center;
align-items: center;
background: #fff;
margin-right: 5px;
}
.main-body {
border: 1px solid #000;
width: 70vw;
}
.header,
.content {
display: flex;
justify-content: center;
background: #fff;
}
.header {
height: 25vh;
margin-bottom: 5px;
}
.content {
align-items:center;
height: 70vh;
}
<div class="parent">
<div class="side">
<h2 class="text">Side</h2>
</div>
<div class="main-body">
<div class="header">
<h2 class="text">Header</h2>
</div>
<div class="content">
<h2 class="text">Content</h2>
</div>
</div>
</div>
I don't think that you deeply understand how flexbox work. You should read more about it. I advice you to read a book called CSS-in Depth. You can download it online from a website called Z-lib. Try to understand the code that I posted for you.
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.parent {
height: 100vh;
border: 20px solid black;
display: flex;
background: pink;
}
.main {
display: flex;
backgound-color: green;
flex-direction: column;
flex: 2
}
.header {
background: cornflowerblue;
}
.side {
flex: 1;
background: rgb(219, 133, 133);
}
.content {
background: rgb(115, 202, 180);
flex: 1
}
.text {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
height: 190px;
color: #fff;
}
</style>
<div class="parent">
<div class="side">
<h2 class="text">Side</h2>
</div>
<div class="main">
<div class="header">
<h2 class="text">Header</h2>
</div>
<div class="content">
<h2 class="text">Content</h2>
</div>
</div>
</div>

How to make nested flex child scrollable

I have a list of messages (which is a flex child) in a container with unknown height and want to make them scrollable. But I cannot find a proper combination of flex-grow: 1, min-height: 0 and other flex tricks to make it working - message list is still bigger than its parent.
When I add overflow-y: auto to its parent - it works but this parent besides messages list includes some content which should not scroll.
Here's my example for this case: https://jsfiddle.net/ecbtrn58/2/
<div class="page">
<div class="messages-section">
<div class="header">Your messages</div>
<div class="content">
<img src="https://http.cat/100" width="70" height="50"/>
<div class="messages-list">
<div class="message">Hi.</div>
<div class="message">Hello.</div>
<div class="message">Good morning.</div>
<div class="message">Yo!</div>
</div>
</div>
</div>
</div>
.page {
background-color: #ddd;
width: 300px;
height: 300px;
.messages-section {
display: flex;
flex-direction: column;
width: 50%;
height: 100%;
background-color: #ccc;
.header {
background: #bbb;
padding: 5px;
}
.content {
display: flex;
flex-direction: column;
align-items: center;
padding: 5px;
.messages-list {
display: flex;
flex-direction: column;
overflow-y: auto;
/* What to add here to make it scrollable? */
.message {
height: 50px;
margin: 10px;
padding: 10px;
background: #1dc497;
}
}
}
}
}
How can I make messages list to scroll?
You have to set the height of .content to 100% and make it scrollable:
.page {
background-color: #ddd;
width: 300px;
height: 300px;
}
.page .messages-section {
display: flex;
flex-direction: column;
width: 50%;
height: 100%;
background-color: #ccc;
}
.page .messages-section .header {
background: #bbb;
padding: 5px;
}
.page .messages-section .content {
display: flex;
flex-direction: column;
align-items: center;
padding: 5px;
height: 100%;
overflow-x: scroll;
}
.page .messages-section .content .messages-list {
display: flex;
flex-direction: column;
overflow-y: auto;
/* What to add here to make it scrollable? */
}
.page .messages-section .content .messages-list .message {
height: 50px;
margin: 10px;
padding: 10px;
background: #1dc497;
}
<div class="page">
<div class="messages-section">
<div class="header">Your messages</div>
<div class="content">
<img src="https://http.cat/100" width="70" height="50" />
<div class="messages-list">
<div class="message">Hi.</div>
<div class="message">Hello.</div>
<div class="message">Good morning.</div>
<div class="message">Yo!</div>
</div>
</div>
</div>
</div>

Stretch columns in two columns layout with shared header using flexbox

I'm using flexbox to create a two-columns layout with a header row.
* {
box-sizing: border-box;
position: relative;
}
.container {
border: 2px solid gray;
display: flex;
flex-wrap: wrap;
height: 300px;
}
.header {
flex-basis: 100%;
border: 2px solid magenta;
height: 50px;
line-height: 50px;
text-align: center;
}
.column1 {
flex-basis: 150px;
/* height: calc(100% - 50px); */
border: 2px solid green;
}
.column2 {
/* height: calc(100% - 70px); */
flex: 1;
border: 2px solid orange;
}
<div class='container'>
<div class='header'>it's a header</div>
<div class='column1'>column 1</div>
<div class='column2'>column 2</div>
</div>
Feel free to see the full example here.
As you can see in the example there is a gap between columns and header. My aim is to stretch columns vertically to fill whole empty space in the container.
I can achieve it by setting height property like calc(100% - <header-height>). Is it the correct way?
I just tried to use "flex" style and set align-items: stretch to the container and align-self: stretch to columns but without success. Did I probably miss something trying to implement it this way?
I think specifying flex-direction as column is appropriate in this case.
The second row is itself a flex element with the flex-direction: row. You can fill the rest of the remaining space using flex: 1, which is equivalent to flex-grow: 1.
* {
box-sizing: border-box;
}
.container {
border: 2px solid gray;
display: flex;
flex-direction: column;
flex-wrap: wrap;
height: 300px;
}
.header {
border: 2px solid magenta;
height: 50px;
line-height: 50px;
text-align: center;
}
.subcontainer {
display: flex;
flex-direction: row;
flex: 1;
}
.column1 {
flex-basis: 150px;
border: 2px solid green;
}
.column2 {
flex: 1;
border: 2px solid orange;
}
<div class='container'>
<div class='header'>it's a header</div>
<div class="subcontainer">
<div class='column1'>column 1</div>
<div class='column2'>column 2</div>
</div>
</div>
Do it like shown below
* {
box-sizing: border-box;
}
body,
html {
height: 100%;
}
.container {
border: 2px solid gray;
display: flex;
flex-direction: column;
height: 100%;
}
.header {
width: 100%;
border: 2px solid magenta;
height: 50px;
line-height: 50px;
text-align: center;
}
.body-container {
display: flex;
width: 100%;
flex: 1;
}
.column1 {
width: 50%;
border: 2px solid green;
}
.column2 {
width: 50%;
border: 2px solid orange;
}
<div class='container'>
<div class='header'>it's a header</div>
<div class="body-container">
<div class='column1'>column 1</div>
<div class='column2'>column 2</div>
</div>
</div>

larger flex child causing previous sibling's padding to be ignored

So I have a flexbox but I'm having trouble understanding why the first child's padding gets ignored when the second child's content overflows.
here's an example when the second child's content aren't overflowing.
body {
padding: 0;
margin: 0;
}
.flex {
display: flex;
flex-direction: column;
width: 100vw;
height: 100vh;
}
.title {
background-color: green;
border-bottom: 10px solid black;
display: flex;
height: 50px;
padding-bottom: 40px;
}
.body {
background-color: blue;
flex: 1;
}
.content {
background-color: red;
height: 10vh;
}
<div class="flex">
<div class="title">
</div>
<div class="body">
<div class="content">
</div>
</div>
</div>
Here's an example when the child's content are overflowing
body {
padding: 0;
margin: 0;
}
.flex {
display: flex;
flex-direction: column;
width: 100vw;
height: 100vh;
}
.title {
background-color: green;
border-bottom: 10px solid black;
display: flex;
height: 50px;
padding-bottom: 40px;
}
.body {
background-color: blue;
flex: 1;
}
.content {
background-color: red;
height: 100vh;
}
<div class="flex">
<div class="title">
</div>
<div class="body">
<div class="content">
</div>
</div>
</div>
you can see in the second example that the title's height has greatly reduced.
It's because you're using flex css, which tries to accommodate all the children. If the title is supposed to not change in size no matter what, you need to set its flex-shrink to 0.
So try changing the css to:
.title {
background-color: green;
border-bottom: 10px solid black;
display: flex;
height: 50px;
padding-bottom: 40px;
flex-shrink: 0;
}

Max Height not working on flexing child with overflow

I have an element with 2 children.
I'm trying to have:
div grow as much as it needs based on 1 of its children
the other always fit the parents height
Thus, I want to avoid setting a height on the parent.
The problem arises when trying to handle overflow of the second child.
Here's the code:
.banner {
display: flex;
background-color: lightblue;
overflow: auto;
border: 4px solid black;
//max-height: 120px; // 1) IF I'M NOT SET THE SCROLL WON'T WORK
}
.constant {
color: white;
flex: 0 0 auto;
width: 200px;
// height: 150px; 2) DISABLED FOR NOW
border: 4px solid yellow;
background-color: olive;
border: 2px solid red;
}
.container {
display: flex;
text-align: center;
}
.main {
max-height: 100%; // 3) I SHOULD STOP MYSELF FROM GROWING MORE THAN MY PARENT
flex: 1;
overflow-y: scroll;
border: 2px solid white;
display: flex;
flex-direction: row-reverse;
align-items: flex-end;
flex-wrap: wrap;
}
.main div {
text-align: center;
width: 200px;
height: 80px;
}
.main-side {
flex: 0 0 auto;
color: white;
background-color: grey;
border: 2px solid yellow;
}
html {
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: inherit;
background-repeat: no-repeat !important;
min-width: 0px;
min-height: 0px;
}
<div class="banner">
<div class="container">
<div class="main">
<div style="background-color:coral;">A</div>
<div style="background-color:lightgoldenrodyellow;">B</div>
<div style="background-color:khaki;">C</div>
<div style="background-color:pink;">D</div>
<div style="background-color:lightgrey;">E</div>
<div style="background-color:lightgreen;">F</div>
</div>
<div class="main-side">I've a fixed size</div>
</div>
<div class="constant">I can grow...and my parent should grow if I grow</div>
</div>
If I set a fixed height on .banner everything works out, but I would like to avoid doing so if possible.
jsfiddle
Thank you.