Make a div in a grid stretch to full height - html

I have a very simple grid layout of the menu, header, and content.
I would like the content (blue box) to stretch vertically. As you can see, the grid element (yellow box) already stretches vertically, but the blue element inside of it (which should be dynamic content) does not.
Is there a way to achieve this 1) without switching the whole grid structure to flexbox and 2) without using calc to give the blue content 100vh minus the header height?
.container {
height: 100vh;
display: grid;
grid-template-rows: min-content 1fr;
grid-template-columns: min-content 1fr;
grid-template-areas: "menu header" "menu content";
box-sizing: border-box;
overflow: hidden;
}
.mainMenuWrapper {
grid-area: menu;
height: auto;
}
.headerWrapper {
grid-area: header;
height: auto;
}
.contentWrapper {
grid-area: content;
overflow-y: auto;
height: auto;
background-color: yellow;
}
.menu {
height: 100vh;
background-color: red;
width: 50px;
}
.header {
height: 80px;
background-color: green;
}
.content {
background-color: blue;
width: 100%;
height: ???
}
<div class="container">
<div class="mainMenuWrapper">
<div class="menu">
menu
</div>
</div>
<div class="headerWrapper">
<div class="header">
header
</div>
</div>
<div class="contentWrapper">
<div class="content">
content
</div>
</div>
</div>
Image:
JSFiddle link: https://jsfiddle.net/the2sj1n/3/

You can apply height: 100% on that blue box .content
body {
margin: 0; /*Removed unexpected margins from browsers' default styles*/
}
.container {
height: 100vh;
display: grid;
grid-template-rows: min-content 1fr;
grid-template-columns: min-content 1fr;
grid-template-areas: "menu header" "menu content";
box-sizing: border-box;
overflow: hidden;
}
.mainMenuWrapper {
grid-area: menu;
height: auto;
}
.headerWrapper {
grid-area: header;
height: auto;
}
.contentWrapper {
grid-area: content;
overflow-y: auto;
height: auto;
background-color: yellow;
}
.menu {
height: 100vh;
background-color: red;
width: 50px;
}
.header {
height: 80px;
background-color: green;
}
.content {
background-color: blue;
width: 100%;
height: 100%; /*The change here*/
}
<div class="container">
<div class="mainMenuWrapper">
<div class="menu">
menu
</div>
</div>
<div class="headerWrapper">
<div class="header">
header
</div>
</div>
<div class="contentWrapper">
<div class="content">
content
</div>
</div>
</div>

Related

make grid child scrollable when it's content is overflown

I'm trying to make the div with the class side-panel-content scrollable when it's content is big, overflow-y: scroll; doesn't work in this case.
The idea here, is to make the full page take the viewport, which works when the .long-content div has no height, but if it has a long height this shouldn't make it's container to grow more, and should be scrollable instead.
Here is a full example:
html, body {
height: 100%;
}
.main {
height: 100%;
display: grid;
grid-template-rows: auto 1fr auto;
}
header, footer {
height: 80px;
background-color: yellow;
}
.content-wrapper {
height: 100%;
width: 100%;
display: grid;
grid-template-rows: auto 1fr;
}
.title {
padding: 24px;
width: 100%;
}
.main-content {
width: 100%;
display: grid;
gap: 24px;
grid-template-columns: 1fr 350px;
justify-content: space-around;
}
.main-panel {
display: grid;
grid-template-rows: auto 1fr;
border: 2px solid red;
}
.side-panel {
height: 100%;
width: 100%;
}
.side-panel-wrapper {
height: 100%;
background: #f8f9fa;
box-shadow: 0px 4px 10px rgb(0 0 0 / 10%);
display: grid;
grid-template-rows: auto 1fr;
}
.side-panel-header {
background-color: #ffffff;
padding: 24px;
border-bottom: 2px solid #e6e9f0;
}
.side-panel-content {
overflow-y: scroll;
}
.long-content {
height: 3000px;
}
<html>
<body>
<div class="main">
<header><h1>Header</h1></header>
<div class="content">
<div class="content-wrapper">
<div class="title">
<h1>Title</h1>
</div>
<div class="main-content">
<div class="main-panel">
<div>
<h2>
Main panel title
</h2>
<p>
Main panel content
</p>
</div>
</div>
<div class="side-panel">
<div class="side-panel-wrapper">
<div class="side-panel-header">
Side panel header
</div>
<div class="side-panel-content">
<div class="long-content">
long content
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<footer><h1>Footer</h1></footer>
</div>
</body>
</html>
The solution for this is to use an absolute positioning for the child div that has a long height.
html, body {
height: 100%;
}
.main {
height: 100%;
display: grid;
grid-template-rows: auto 1fr auto;
}
header, footer {
height: 80px;
background-color: yellow;
}
.content-wrapper {
height: 100%;
width: 100%;
display: grid;
grid-template-rows: auto 1fr;
}
.title {
padding: 24px;
width: 100%;
}
.main-content {
width: 100%;
display: grid;
gap: 24px;
grid-template-columns: 1fr 350px;
justify-content: space-around;
}
.main-panel {
display: grid;
grid-template-rows: auto 1fr;
border: 2px solid red;
}
.side-panel {
height: 100%;
width: 100%;
}
.side-panel-wrapper {
height: 100%;
background: #f8f9fa;
box-shadow: 0px 4px 10px rgb(0 0 0 / 10%);
display: grid;
grid-template-rows: auto 1fr;
}
.side-panel-header {
background-color: #ffffff;
padding: 24px;
border-bottom: 2px solid #e6e9f0;
}
.side-panel-content {
overflow: auto;
position: relative;
height: 100%;
}
.long-content {
position: absolute;
top: 0;
height: 3000px;
}
<html>
<body>
<div class="main">
<header><h1>Header</h1></header>
<div class="content">
<div class="content-wrapper">
<div class="title">
<h1>Title</h1>
</div>
<div class="main-content">
<div class="main-panel">
<div>
<h2>
Main panel title
</h2>
<p>
Main panel content
</p>
</div>
</div>
<div class="side-panel">
<div class="side-panel-wrapper">
<div class="side-panel-header">
Side panel header
</div>
<div class="side-panel-content">
<div class="long-content">
long content
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<footer><h1>Footer</h1></footer>
</div>
</body>
</html>

Display an overlay to fit parent when it is a grid area

I have a grid (containers) to display a navbar, a main-container and a footer. In the main-container, it is also a grid to display 3 children elements in a custom disposition using grid-area. Everything works properly.
Now, I would like to display an overlay div (overlay) which has to fit the whole main-container. To do so, I just add an additional div in the main-container with the property height: 100%; width: 100%; and play with the position property without success. Indeed, position: absolute or position: fixed make it too big and I don't want to set a fixed width or height since the layout can be responsive. And with position: relative, it is not working neither.
Do you have any idea, how I could make the overlay display above the main-container while keeping the properties width: 100%; height: 100%;?
#containers {
display: grid;
grid-template-areas: "navbar main" "navbar footer";
grid-template-rows: calc(100% - 32px) 32px;
grid-template-columns: 56px calc(100% - 56px);
}
#navbar-container {
grid-area: navbar;
background-color: blue;
}
#main-container {
grid-area: main;
height: 100%;
width: 100%;
display: grid;
grid-template-areas:
'child1 child2'
'child1 child3';
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
}
#footer-container {
grid-area: footer;
background-color: black;
}
#child1,
#child2,
#child3 {
padding: 10px;
}
#child1 {
grid-area: child1;
background-color: red;
}
#child2 {
grid-area: child2;
background-color: purple;
}
#child3 {
grid-area: child3;
background-color: pink;
}
#footer-container {
grid-area: footer;
}
.modal {
width: 100%;
height: 100%;
background-color: grey;
opacity: 0.5;
position: absolute;
}
<div id="containers">
<div id="navbar-container"></div>
<div id="main-container">
<div id="child1">
<span>CHILD 1</span>
</div>
<div id="child2">
<span>CHILD 2</span>
</div>
<div id="child3">
<span>CHILD 3</span>
</div>
<div class="modal">
MODAL
</div>
</div>
<div id="footer-container"></div>
</div>
Just needed to make the parent relative and the modal absolute.
#containers {
display: grid;
grid-template-areas: "navbar main" "navbar footer";
grid-template-rows: calc(100% - 32px) 32px;
grid-template-columns: 56px calc(100% - 56px);
}
#navbar-container {
grid-area: navbar;
background-color: blue;
}
#main-container {
grid-area: main;
height: 100%;
width: 100%;
display: grid;
grid-template-areas:
'child1 child2'
'child1 child3';
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
position: relative;
}
#footer-container {
grid-area: footer;
background-color: black;
}
#child1,
#child2,
#child3 {
padding: 10px;
}
#child1 {
grid-area: child1;
background-color: red;
}
#child2 {
grid-area: child2;
background-color: purple;
}
#child3 {
grid-area: child3;
background-color: pink;
}
#footer-container {
grid-area: footer;
}
.modal {
width: 100%;
height: 100%;
background-color: grey;
opacity: 0.5;
position: absolute;
}
<div id="containers">
<div id="navbar-container"></div>
<div id="main-container">
<div id="child1">
<span>CHILD 1</span>
</div>
<div id="child2">
<span>CHILD 2</span>
</div>
<div id="child3">
<span>CHILD 3</span>
</div>
<div class="modal">
MODAL
</div>
</div>
<div id="footer-container"></div>
</div>

Header Sticky Position in CSS Grid Layout

I'm learning CSS Grid layout and i have a problem about positioning.
What i want is to create a page layout composed by a left-side menu, top-bar menu and a main content page like the image below:
I have been able to achieve the goal, but now i want to fix the position of the top bar and sidebar while main content is scrolling.
I set position:sticky to both containers but it does not working.
How can i fix?
Here is my code:
* {
margin: 0 !important;
padding: 0 !important;
box-sizing: border-box !important;
}
.grid-container {
display: grid;
grid-template-columns: auto 1fr;
grid-template-rows: 10% 100vh;
grid-template-areas:
"LeftMenu TopMenu"
"LeftMenu Main";
}
.LeftMenu {
background-color: #a4a4a4;
grid-area: LeftMenu;
width: 200px;
height: 100%;
}
.TopMenu {
background-color: #d49494;
grid-area: TopMenu;
width: 100%;
height: 100%;
display: flex;
}
.Main {
background-color: #8990eb;
grid-area: Main;
width: 100%;
height: 100vh;
}
<div class="xdg-component-appnav-menu">
<div class="grid-container">
<div class="LeftMenu">left menu</div>
<div class="TopMenu">top menu</div>
<div class="Main">
<p style="padding-bottom: 1000px;">Content</p>
</div>
</div>
</div>
You don't need position: sticky. It's extra complication and still isn't fully supported by some browsers.
Just use overflow: auto on the main container.
.grid-container {
display: grid;
height: 100vh;
grid-template-columns: 200px 1fr;
grid-template-rows: 10% 90%;
grid-template-areas:
"LeftMenu TopMenu"
" LeftMenu Main ";
}
.LeftMenu {
grid-area: LeftMenu;
background-color: #a4a4a4;
}
.TopMenu {
grid-area: TopMenu;
background-color: #d49494;
}
.Main {
grid-area: Main;
overflow: auto; /* key adjustment */
background-color: #8990eb;
}
body {
margin: 0;
}
<div class="xdg-component-appnav-menu">
<div class="grid-container">
<div class="LeftMenu">left menu</div>
<div class="TopMenu">top menu</div>
<div class="Main">
<p style="height: 1000px;">Content</p>
</div>
</div>
</div>

Allow one grid item to scroll with fixed header and sidebar

I have a grid layout with two columns and two rows. A sticky left nav, a sticky header, and content that will live in the bottom right corner of the grid.
What I have now is nearly there, but I would like the .content div to use scroll when content extends beyond the screen. I thought I would be able to just use overflow: auto, but that isn't working. Is what I have close?
body {
margin: 0;
overflow: hidden;
}
.page {
display: grid;
grid-template-rows: 55px auto;
grid-template-columns: 20vh auto;
grid-template-areas: "nav header" "nav content";
}
.nav {
grid-area: nav;
background-color: blue;
}
.header {
grid-area: header;
background-color: grey;
}
.content {
grid-area: content;
height: 1000px; // This is dynamic
background-color: red;
}
<div class="page">
<div class="nav">Side nav</div>
<div class="header">Header</div>
<div class="content">
<h1>title</h1>
</div>
<div>
JS fiddle
For overflow: auto to work (i.e., for scrollbars to render) browsers need a trigger. This trigger is usually a height / width limitation that forces an overflow condition, which launches the scrollbars.
Trigger conditions vary among browsers. They also vary among CSS technologies, such as flex, grid and block layouts.
In this particular case, there are several logical places to establish an overflow condition, but none of them work.
You could target the grid item, as you have tried:
.content {
height: 1000px
overflow: auto;
}
But it doesn't work. No scrollbar appears on the fluid item.
body {
margin: 0;
/* overflow: hidden; */
}
.page {
display: grid;
grid-template-rows: 55px auto;
grid-template-columns: 20vh auto;
grid-template-areas: "nav header"
"nav content";
}
.nav {
grid-area: nav;
background-color: aqua;
}
.header {
grid-area: header;
background-color: lightgrey;
}
.content {
grid-area: content;
height: 1000px;
overflow: auto;
background-color: red;
}
<div class="page">
<div class="nav">Side nav</div>
<div class="header">Header</div>
<div class="content">
<h1>title</h1>
</div>
<div>
You could target the row itself, as I tested:
.page {
display: grid;
grid-template-rows: 55px 1000px;
}
.content {
overflow: auto;
}
But that doesn't work either. Still no scrollbar on the fluid item.
body {
margin: 0;
/* overflow: hidden; */
}
.page {
display: grid;
grid-template-rows: 55px 1000px;
grid-template-columns: 20vh auto;
grid-template-areas:
"nav header"
"nav content";
}
.nav {
grid-area: nav;
background-color: aqua;
}
.header {
grid-area: header;
background-color: lightgrey;
}
.content {
overflow: auto;
grid-area: content;
background-color: red;
}
<div class="page">
<div class="nav">Side nav</div>
<div class="header">Header</div>
<div class="content">
<h1>title</h1>
</div>
<div>
So I targeted a child of the grid item. DING DING DING! That worked.
No need for fixed positioning. No need for sticky positioning. This works across all browsers that support Grid Layout.
body {
margin: 0;
}
.page {
display: grid;
grid-template-rows: 55px calc(100vh - 55px); /* height limitation on second row */
grid-template-columns: 20vh auto;
grid-template-areas: "nav header"
"nav content";
}
.nav {
grid-area: nav;
background-color: aqua;
}
.header {
grid-area: header;
background-color: lightgrey;
}
.content {
grid-area: content;
background-color: red;
overflow: auto; /* overflow condition on parent */
}
article {
height: 1000px; /* height set on child; triggers scroll */
}
<div class="page">
<div class="nav">Side nav</div>
<div class="header">Header</div>
<div class="content">
<article><!-- new section for content -->
<h1>title</h1>
</article>
</div>
<div>
jsFiddle demo
Browser support is not 100%, but what about actually using sticky instead of fixed positioning? (now tested in Chrome) You won't have to deal with hard-coded margins.
One of the issues you'll still have to deal with, what to do when the content in your sidebar (.nav > div) Is higher than your viewport.
body {
margin: 0;
}
.page {
display: grid;
grid-template-rows: 55px auto;
grid-template-columns: 3.5rem auto;
grid-template-areas: "nav header" "nav content";
}
.nav {
grid-area: nav;
background-color: blue;
}
.nav > div {
position: sticky;
top: 0;
}
.header {
grid-area: header;
background-color: grey;
position: sticky;
top: 0;
min-height: 3.5rem;
}
.content {
grid-area: content;
min-height: 1000px;
background-color: red;
}
<div class="page">
<div class="nav">
<div>Side nav</div>
</div>
<div class="header">Header</div>
<div class="content">
<h1>title</h1>
</div>
<div>
I have included the change log to see where the code needs to be change in order to get an understanding. Also the full code snippet is available below. Hope this is what you expect.
Change log
*Remove body { overflow: hidden; }
*Change .page { grid-template-columns: 3.5rem auto; }
*Added
.nav { position: fixed;
top: 0;
bottom:0;}
*Added
.header { position: fixed;
margin-left: 3.5rem;
width: 100%;
height: 3.5rem; }
Full Code
body {
margin: 0;
}
.page {
display: grid;
grid-template-rows: 55px auto;
grid-template-columns: 3.5rem auto;
grid-template-areas:
"nav header"
"nav content";
}
.nav {
position: fixed;
top: 0;
bottom:0;
grid-area: nav;
background-color: blue;
}
.header {
grid-area: header;
background-color: grey;
position: fixed;
margin-left: 3.5rem;
width: 100%;
height: 3.5rem;
}
.content {
grid-area: content;
height: 1000px;
background-color: red;
}
<div class="page">
<div class="nav">Side nav</div>
<div class="header">Header</div>
<div class="content">
<h1>title</h1>
</div>
<div>

Make grid item consume the space of another item that has been removed

Essentially what I need to happen is to set up a grid, but if one of the elements is missing, another element stretches to fill the space.
This is an example Pen of where I'm currently at:
https://codepen.io/Rockster160/pen/JMLaXY
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
div {
box-sizing: border-box;
}
.grid {
height: 100%;
width: 100%;
background: white;
border: 2px solid red;
display: grid;
grid-template-columns: 250px auto;
grid-template-rows: 100px auto 50px;
grid-template-areas: "sidebar header"
"sidebar content"
"sidebar footer";
}
.sidebar {
grid-area: sidebar;
background: green;
}
.header {
grid-area: header;
background: lightblue;
}
.content {
grid-area: content;
background: blue;
border: 5px solid black;
}
.footer {
grid-area: footer;
background: orange;
}
<div class="grid">
<div class="sidebar"></div>
<div class="header"></div>
<div class="content"></div>
<!-- <div class="footer"></div> -->
</div>
footer is an optional element, so when it doesn't exist (commented out as in the code) then content should stretch and line up with the bottom of sidebar.
I've tried a variety of different combinations using min/max content and different auto placements, but no luck. I thought if I had multiple elements named content it might work as well, but no luck there either.
Any help is greatly appreciated!
You are forcing the 3rd row to be 50px in the grid style.
Change it to be adapted to the content, and set the 50px as height in the footer itself:
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
div {
box-sizing: border-box;
}
.grid {
height: 100%;
width: 100%;
background: white;
border: 2px solid red;
display: grid;
grid-template-columns: 250px auto;
grid-template-rows: 100px auto max-content; /* changed last 50px to max-content*/
grid-template-areas: "sidebar header"
"sidebar content"
"sidebar footer";
}
.sidebar {
grid-area: sidebar;
background: green;
}
.header {
grid-area: header;
background: lightblue;
}
.content {
grid-area: content;
background: blue;
border: 5px solid black;
}
.footer {
grid-area: footer;
background: orange;
height: 50px; /* added */
}
<div class="grid">
<div class="sidebar"></div>
<div class="header"></div>
<div class="content"></div>
<!-- <div class="footer"></div> -->
</div>
And another posibility, thanks to Michael_B. The sintax of grid-template-rows is clearer:
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
div {
box-sizing: border-box;
}
.grid {
height: 100%;
width: 100%;
background: white;
border: 2px solid red;
display: grid;
grid-template-columns: 250px auto;
grid-template-rows: 100px 1fr auto;
grid-template-areas: "sidebar header"
"sidebar content"
"sidebar footer";
}
.sidebar {
grid-area: sidebar;
background: green;
}
.header {
grid-area: header;
background: lightblue;
}
.content {
grid-area: content;
background: blue;
border: 5px solid black;
}
.footer {
grid-area: footer;
background: orange;
height: 50px; /* added */
}
<div class="grid">
<div class="sidebar"></div>
<div class="header"></div>
<div class="content"></div>
<div class="footer"></div>
</div>
The easiest way to do this is to use the :last-child selector:
.content:last-child {
grid-row: content / footer;
}
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
div {
box-sizing: border-box;
}
.grid {
height: 100%;
width: 100%;
background: white;
border: 2px solid red;
display: grid;
grid-template-columns: 250px auto;
grid-template-rows: 100px auto 50px;
grid-template-areas: "sidebar header" "sidebar content" "sidebar footer";
margin-bottom: 2rem;
}
.sidebar {
grid-area: sidebar;
background: green;
}
.header {
grid-area: header;
background: lightblue;
}
.content {
grid-area: content;
background: blue;
border: 5px solid black;
}
.content:last-child {
grid-row: content / footer;
}
.footer {
grid-area: footer;
background: orange;
}
<div class="grid">
<div class="sidebar"></div>
<div class="header"></div>
<div class="content"></div>
<!-- <div class="footer"></div> -->
</div>
<div class="grid">
<div class="sidebar"></div>
<div class="header"></div>
<div class="content"></div>
<div class="footer"></div>
</div>
Or, alternatively, we could reverse the order of the .content and .footer elements in the HTML (as below) and use the CSS negation operator (:not()) to determine that the .content element should take up extra space if it is not preceded by a .footer element:
:not(.footer) + .content {
grid-row: content/footer;
}
which styles a .content element that is not immediately preceded by a .footer sibling in such a way that it starts in the grid-row identified by content and ends in the grid-row identified by footer:
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
div {
box-sizing: border-box;
}
.grid {
height: 100%;
width: 100%;
background: white;
border: 2px solid red;
display: grid;
grid-template-columns: 250px auto;
grid-template-rows: 100px auto 50px;
grid-template-areas: "sidebar header" "sidebar content" "sidebar footer";
}
.sidebar {
grid-area: sidebar;
background: green;
}
.header {
grid-area: header;
background: lightblue;
}
.content {
grid-area: content;
background: blue;
border: 5px solid black;
}
:not(.footer)+.content {
grid-row: content/footer;
}
.footer {
grid-area: footer;
background: orange;
}
<div class="grid">
<div class="sidebar"></div>
<div class="header"></div>
<div class="content"></div>
<!-- <div class="footer"></div> -->
</div>
<div class="grid">
<div class="sidebar"></div>
<div class="header"></div>
<div class="content"></div>
<div class="footer"></div>
</div>
References:
grid-row.
:last-child.
Negation pseudo-class:not().
Sometimes things are simpler with flexbox.
Since your container has a defined height (the viewport), you can use flex-flow: column wrap to create both columns.
Then use flex: 1 on the content item, which tells it to consume free space.
When the footer is present, the content makes space for it. When the footer is not present, the content consumes all space.
.grid {
display: flex;
flex-flow: column wrap;
height: 100vh;
background: white;
border: 2px solid red;
}
.sidebar {
flex: 0 0 100%;
width: 250px;
background: green;
}
.header {
flex: 0 0 100px;
width: calc(100% - 250px);
background: lightblue;
}
.content {
flex: 1;
width: calc(100% - 250px);
border: 5px solid black;
background: blue;
}
.footer {
flex: 0 0 50px;
width: calc(100% - 250px);
background: orange;
}
body { margin: 0; }
div { box-sizing: border-box; }
<!-- WITH FOOTER -->
<div class="grid">
<div class="sidebar"></div>
<div class="header"></div>
<div class="content"></div>
<div class="footer"></div>
</div>
<hr>
<!-- WITHOUT FOOTER -->
<div class="grid">
<div class="sidebar"></div>
<div class="header"></div>
<div class="content"></div>
</div>
Change your .grid class to
.grid {
display: grid;
grid-template-columns: 250px auto;
grid-template-rows: 100px auto 50px;
grid-template-areas:
"sidebar header"
"sidebar content"
"sidebar content";
}
when you comment your footer tag as in your pen, grid is still waiting to have a footer element there, so it's kinda "saving space" for this element there