Sidebar with two flexbox navs top and bottom - css

I´m trying to align two navigations inside a sidebar –with 100% viewport height – by use of flexbox.
the red box should be placed on the top of it´s sidebar parent
the blue box on the bottom.
In case the red navigation grows and the space between both is to little the sidebar should be scrollable in y-axis. What I´ve tried is setting top and bottom margin for both without luck. Can somebody help me out ?
Thanks!
html, body {
margin: 0;
}
* {
box-sizing: border-box;
}
.sidebar {
height: 100vh;
width: 300px;
background: #ccc;
padding: 10px;
overflow-y: scroll;
}
.sidebar__top {
background: red;
height: 200px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.sidebar__bottom {
background: blue;
height: 100px;
margin-top: auto;
}
<aside class="sidebar">
<nav class="sidebar__top"></nav>
<nav class="sidebar__bottom"></nav>
</aside>
Here is my fiddle: http://jsfiddle.net/1dw7h2sp/1/

There are probably other ways to do this. In short I did the following:
Wrap your elements with a parent that is able to grow in size (.sidebar__wrapper)
Set the min-height instead of height so it can grow
Use flex-grow if you want an element to fill out the remaining space.
html, body {
margin: 0;
}
* {
box-sizing: border-box;
}
.sidebar {
height: 100vh;
width: 300px;
background: #ccc;
overflow-y: scroll;
margin: 0;
padding: 0;
}
/* set up a wrapper that can grow in size */
.sidebar__wrapper {
height: auto;
min-height: 100vh;
width: 100%;
background: #808080;
padding: 10px;
margin: 0;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.sidebar__top {
background: red;
min-height: 200px;
margin-bottom: 10px;
/* this fills up the remaining space */
flex-grow: 1;
}
.sidebar__bottom {
background: blue;
min-height: 100px;
}
<aside class="sidebar">
<div class="sidebar__wrapper">
<nav class="sidebar__top" contenteditable="true">
<p>test</p>
</nav>
<nav class="sidebar__bottom"></nav>
</div>
</aside>

Related

Child of Full Screen Container is Trimmed If Taller Than Parent [duplicate]

This question already has answers here:
Can't scroll to top of flex item that is overflowing container
(12 answers)
Closed 4 years ago.
I must be forgetting something fundamental with my vertically and horizontally centered flexbox.
The container is within a parent with vertical scroll, and when the container becomes too tall, it grows beyond the parent top, clipping the content. The bottom stays put.
Try adjusting the height of the view or adding more lines to see it in action.
body,
html {
height: 100%;
width: 100%;
margin: 0;
}
* {
box-sizing: border-box;
}
#wrapper {
background: grey;
height: 100%;
width: 100%;
max-height: 100%;
display: flex;
overflow-y: auto;
align-items: center;
justify-content: center;
}
#box {
margin: 30px 0;
background: white;
border: 1px solid #dfdfdf;
}
<div id="wrapper">
<div id="box">
First line
<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br> Last linje
</div>
</div>
How do I keep it from getting clipped? Additionally I'm trying to have a 30px margin above and below the container.
Thanks!
You forgot nothing but you simply need to understand what is happening. First you made your wrapper to be 100% height of screen and then you made the box to be centred vertically and horizontally. When the box has a big height you will have something like this:
Now, when you add overflow-y: auto you will create a scroll that will start from the top of the wrapper until the bottom overflowed content. So it will be like this:
That's why you are able to scroll to the bottom to see the bottom part and not able to see the top part.
To avoid this, use margin:auto to center your element and in this case we will have two situations:
When box-height < wrapper-height we will have the space spread equally on each side because of the margin:auto thus your element will be centred like expected.
When box-height > wrapper-height we will have the normal behavior and your element will overflow and his top edge will stick to the top edge of the wrapper.
You may also notice the same can happen horizontally that's why I will use margin to center on both directions.
body,
html {
height: 100%;
width: 100%;
margin: 0;
}
* {
box-sizing: border-box;
}
#wrapper {
background: grey;
height: 100%;
width: 100%;
max-height: 100%;
padding:30px 0;
display: flex;
overflow-y: auto;
}
#box {
margin: auto;
background: white;
border: 1px solid #dfdfdf;
}
<div id="wrapper">
<div id="box">
First line
<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br> Last linje
</div>
</div>
I think what you want is to make your flex item (#box) have a height and set it's overflow, not the flex container. Also, to add your 30px above and below I would remove the margin from the box and instead add padding to the container.
So, updated styles would look like this:
#wrapper {
background: grey;
height: 100%;
width: 100%;
max-height: 100%;
display: flex;
align-items: center;
justify-content: center;
padding: 30px 0; /*added*/
}
#box {
background: white;
border: 1px solid #dfdfdf;
overflow-y: auto; /*added*/
height: 100%; /*added*/
}
body,
html {
height: 100%;
width: 100%;
margin: 0;
}
* {
box-sizing: border-box;
}
#wrapper {
background: grey;
height: 100%;
width: 100%;
max-height: 100%;
display: flex;
align-items: center;
justify-content: center;
padding: 30px 0;
}
#box {
background: white;
border: 1px solid #dfdfdf;
overflow-y: auto;
height: 100%;
}
<div id="wrapper">
<div id="box">
First line
<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br>line<br> Last linje
</div>
</div>
I think you set the top margin in the box class which extends the height of the container. You can maybe set it to padding instead of margin. Hope this helps. Thanks.

How to show scroll bars in a div with the same width as its header

I am using CSS to set the style for two sibling DIVs (a div.header and a div.body) so that their width is equal and the minimum width of the header is the minimum width of its div.wrapper so that scrollbars will appear on the browser window when narrower than the header.
BUT I am also trying to have scrollbars appear on div.body when the div.content within it expands outside of div.body. Currently the div.body expands to fit its content. I would also like div.body to fill the vertical space when div.content is small.
Edit: It's difficult to explain, so here is a picture that might help. Left side is when the full .header width is visible. Right side adds scrollbars at the outer level (browser window) when narrower than the .header text.
I am not confident that my current approach is on the right path, but I have the header enforcing the minimum width based on https://stackoverflow.com/a/48226415
.content {
background-color: pink;
margin: 1rem;
width: 100px;
}
body {
margin: 0;
}
.root {
background-color: yellow;
position: absolute;
top: 0;
bottom: 0;
width: 100%;
height: 100%;
overflow-x: auto;
overflow-y: hidden;
}
.wrapper {
background-color: lightgray;
overflow: hidden;
display: inline-block;
min-width: 100%;
min-height: 100%;
}
.header {
background-color: lightgreen;
white-space: nowrap;
}
.body {
background-color: lightblue;
display: flex;
flex-grow: 1;
overflow-x: scroll;
overflow-y: auto;
}
<div class="root">
<div class="wrapper">
<div class="header">
header header header header header header header header header header header header
</div>
<div class="body">
<div class="content">
content
</div>
</div>
</div>
</div>
Here is my JSFiddle with comments in the .content style at the top of the CSS.
Flex
If I understand right, I think might have found the issue. The display: flex; in the .body class is preventing you from getting any scrollbar.
Flex makes the element aware of the space around it and will size itself accordingly, no matter what width or overflow values you set for it or its parents.
.content {
background-color: pink;
margin: 1rem;
width: 1000px;
}
body {
margin: 0;
}
.root {
background-color: yellow;
position: absolute;
top: 0;
bottom: 0;
width: 100%;
height: 100%;
overflow-x: auto;
overflow-y: hidden;
}
.wrapper {
background-color: lightgray;
overflow: hidden;
display: inline-block;
min-width: 100%;
min-height: 100%;
}
.header {
background-color: lightgreen;
white-space: nowrap;
}
.body {
background-color: lightblue;
width: 500px;
overflow-x: scroll;
overflow-y: auto;
}
<div class="root">
<div class="wrapper">
<div class="header">
header header header header header header header header header header header header
</div>
<div class="body">
<div class="content">
content
</div>
</div>
</div>
</div>
I gave the content and body some more width to make it more obvious that now a scrollbar WILL appear.
Here is the same example in a fiddle.
1. Force the .body width to both 0 and 100%
Force the width of .body class to be 0 so the width of the container is defined by the .header (the .body doesn't contribute to the width)
Then force the width of the .body again to be 100% using min-width
This gets the .body and its scrollbars to behave as desired relative to the .wrapper.
(I got inspiration for this step from this not-obviously related answer: https://stackoverflow.com/a/55041133/186818)
2. Change the .wrapper to inline-flex and fill the visible space
Set the .body height to 100% to fill the vertical space of the .wrapper.
Change the .wrapper to inline-flex with:
display: inline-flex;
flex-grow: 1;
flex-direction: column;
/* fill the visible space */
min-width: 100%;
height: 100%;
This stacks the .header over the .body and expands to fill the space without extending past the visible boundaries.
Revised Code Snippet
.content {
background-color: pink;
margin: 1rem;
/* example large content within the .body */
height: 200px;
width: 1000px;
}
body { margin: 0; }
.root {
background-color: yellow;
position: absolute;
top: 0;
bottom: 0;
width: 100%;
height: 100%;
/* allow scrolling to see all of header */
overflow-x: auto;
/* y-direction scroll should be within .body */
overflow-y: hidden;
}
.wrapper {
background-color: lightgray;
overflow: hidden;
/* Do this: */
display: inline-flex;
flex-grow: 1;
flex-direction: column;
/* fill the visible space */
min-width: 100%;
height: 100%;
/* Not this: */
/* display: inline-block; */
/* min-height: 100%; */
}
.header {
background-color: lightgreen;
white-space: nowrap;
}
.body {
background-color: lightblue;
/* Do this: */
display: block;
width: 0;
min-width: 100%;
height: 100%;
/* Not this: */
/* display: flex; */
/* flex-grow: 1; */
overflow-x: scroll;
overflow-y: auto;
}
<div class="root">
<div class="wrapper">
<div class="header">
header header header header header header header header header header header header
</div>
<div class="body">
<div class="content">
content
</div>
</div>
</div>
</div>
And here is a link to the JSFiddle.

An alternative design without a scrollbar caused by padding

The code that appears below creates the following layout:
The important part is that the content, although centered on the screen when not overflowing, never overlaps the navbar and has its own scrollbar:
The problem is that this layout is achieved with the help of padding (marked by a comment in the code below), which results in the additional scrollbar on the right of the screen.
How can I design the same layout that would have only one scrollbar - the one in the content?
Please note that the solution should not break the following details:
The rounded corners and the shadow.
The title in the content block not participating in scrolling.
The image covering the whole scrollable content, so it scrolls together with the content.
In addition, it would be great if this can be achieved without as many nested div's as I have right now.
Edit: I am ready to go with the suggestion of #JHeth in the comments section. However, I would still be interested if someone can come up with an alternative design that does not rely on padding for centering.
Here is the code (CodePen):
* {
padding: 0;
margin: 0;
}
html {
font-size: 62.5%;
}
:root {
--navbar-height: 3rem;
}
.navbar {
width: 100%;
text-align: center;
font-size: 2rem;
line-height: var(--navbar-height);
background-color: lightgreen;
}
.centering {
position: absolute;
inset: var(--navbar-height) 0 0 0;
display: flex;
flex-direction: column;
align-items: center;
& .auto-margin {
margin: auto;
// For scrollable content
display: flex;
max-height: 100%;
padding-bottom: calc(var(--navbar-height)); // Causes scrolling
}
}
.content-block {
display: flex;
flex-direction: column;
.title {
font-size: 2rem;
position: sticky;
}
.content-outer-container {
display: flex;
flex-direction: column;
overflow-y: auto;
border-radius: 1em;
box-shadow: 0 1em 2em rgba(black, 0.4);
.content-container {
width: 300px;
overflow-y: auto;
.content {
position: relative;
padding: 1em;
&:before {
content: "";
z-index: -1;
position: absolute;
top: 0;
left: 0;
right: 0;
height: 100%;
background-position: center;
background-image:
url(http://www.freegreatpicture.com/files/147/18380-hd-color-background-wallpaper.jpg);
}
}
}
}
}
<div class="navbar">An Amazing Navbar</div>
<div class="centering">
<div class="auto-margin">
<div class="content-block">
<div class="title">My Title</div>
<div class="content-outer-container">
<div class="content-container">
<div class="content">
<h1>Line1</h1>
<h1>Line2</h1>
<h1>Line3</h1>
<h1>Line4</h1>
<h1>Line5</h1>
</div>
</div>
</div>
</div>
</div>
</div>
simply remove max-height:100%; to .centering .auto-margin and it'll works!
.centering .auto-margin {
margin: auto;
display: flex;
/* max-height: 100%; */
padding-bottom: calc(var(--navbar-height));
}

Fill available height, then scroll when no more room

I would like two elements (divs) in a column. The first has a fixed height, and the second I want to fill all remaining height. If the content of the second element exceeds the space left, I want to scroll its contents, and not have the second element take up more space (which causes the parent to scroll).
Image (current)
For example, the "first element" is the search bar in this photo. Fixed height, and remains on top.
The "second element" is the data table. As you can see at the bottom, the table contents extend the height of the page, and the entire page becomes scrollable. This is not what I am looking for.
Image (desired) I would like the table container to behave similarly to this red box. It fills it's remaining height, and when it contains content that overflows, I would like just that element to scroll. Only scroll the content within the confines of the red box.
I have seen many examples similar to this, but all of them have a specified height for the "second element", even if it is a vh property. vh doesn't work for me since the 100% isn't the entire viewport.
I've been using flexbox to try and achieve this, and I get close, but I've only ever been able to either specify a height for the "second element", or have it grow to fill available space, but then exceed it, and overflow the whole container.
The below code is very close to desired behavior, except when the viewport becomes to small, "box 2" goes off screen and the whole thing scrolls, I want the content in "box 2" to scroll.
html,
body {
height: 100%;
}
.container {
height: 100%;
min-height: 100%;
display: flex;
flex-direction: column;
.box {
text-align: center;
color: white;
font-family: sans-serif;
font-size: 36px;
padding: 20px;
display: flex;
flex-direction: column;
justify-content: center;
}
.box-1 {
background-color: green;
height: 60px;
}
.box-2 {
background-color: blue;
flex: 1;
}
<div class="container">
<div class="box box-1">box 1</div>
<div class="box box-2">box 2</div>
</div>
Just apply an overflow:auto on the second div and a max-height to the container.
* {
margin: 0;
padding: 0;
}
html,
body {
height: 100%;
}
.container {
max-height: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
.box {
text-align: center;
color: white;
font-family: sans-serif;
font-size: 36px;
padding: 20px;
justify-content: center;
}
.box-1 {
background-color: green;
height: 60px;
}
.box-2 {
background-color: blue;
flex: 1;
overflow: auto;
}
.expando {
height: 1000px;
/* for demo */
}
<div class="container">
<div class="box box-1">box 1</div>
<div class="box box-2">box 2
<div class="expando"></div>
</div>
</div>
html,
body {
height: 100%;
margin: 0;
}
.container {
height: 100vh;
display: flex;
flex-direction: column;
}
.box {
text-align: center;
color: white;
font-family: sans-serif;
font-size: 36px;
padding: 20px;
}
.box-1 {
background-color: green;
height: 60px;
}
.box-2 {
background-color: blue;
height: 100%;
overflow: auto;
}

Resize flex item based on its content

Sorry, another flexbox related question :)
I have two flex elements :
A container (red) containing a centered div (yellow)
A footer (blue) with an undefined height
The red container has a flex-grow:1 attribute, forcing it to take the remaining space on the screen
The issue happens when the yellow element is bigger than the screen size. I would like my red container to grow based on its content. Any idea of how I could do that ?
HTML:
<div class="container">
<div class="content"></div>
</div>
<div class="footer"></div>
CSS:
body,
html {
margin: 0;
height: 100%;
display: flex;
flex-direction: column;
}
.container {
flex-grow: 1;
display: flex;
align-items: center;
justify-content: center;
background: red;
}
.content {
background: yellow;
height: 2000px;
width: 100px;
}
.footer {
flex-shrink: 0;
height: 50px;
background-color: blue;
}
https://codepen.io/stepinsight/pen/roRVGQ
== EDIT ==
Andre helped me find the answer, thanks heaps !
The only thing you need to change in the code above is to replace height by min-height and the % by vh for the body/html tags 🎉
body,
html {
margin: 0;
min-height: 100vh;
display: flex;
flex-direction: column;
}
Simply remove the height property on the body element and add height: 100% to html
* { box-sizing: border-box; }
html {
height: 100%
}
body {
display: flex;
flex-direction: column;
margin: 0;
padding: 0;
}
.container {
display: flex;
align-items: center;
justify-content: center;
background: red;
}
.content {
background: yellow;
height: 2000px;
width: 100px;
}
.footer {
height: 50px;
background-color: blue;
}
Corrected: https://codepen.io/ferreirandre/pen/maoVvb
Feel free to play around with the height of .content

Resources