I want the graph div to have a height that stretches all the way to the end of it's parent (graph-container) container, but not extend past it. I have tried setting graph's height to both 100% and inherit, but both of them result in it extending past it's parent container's bottom edge (I don't want to use overflow: hidden). Is there a way to set the height property on graph so it automatically sets its height to the right value?
Current code:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-size: 20px;
}
.wrapper {
height: 100vh;
width: 100vw;
display: flex;
justify-content: center;
}
.graph-container {
border: 3px solid black;
height: 60vh;
width: 70vw;
margin-top: 10vh;
}
.graph {
height: 100%;
width: 100%;
border: 3px solid red;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="wrapper">
<div class="graph-container">
<h1>Graph Container</h1>
<div class="graph">Graph</div>
</div>
</div>
</body>
</html>
The overflow you see happens because there is already an element taking up some space of the parent, so height: 100%
plus the height of the other element will obviously exceed the parents height.
There are different ways to solve the problem.
I recommend one of these two ways:
However, you can also solve them using these ways:
calc()
The parent needs display: flex
and flex-direction: column
to lay out its children in a column.
The child that should grow to take up the remaining space needs flex-grow: 1
.
.graph-container {
display: flex;
flex-direction: column;
}
.graph {
flex-grow: 1;
}
/* Ignore; only for displaying the example */
* {
margin: 0;
font-size: 24px;
}
html, body {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.graph-container, .graph {
border: 3px solid;
box-sizing: border-box;
}
.graph-container {
border-color: black;
height: 60vh;
width: 70vw;
}
.graph {
border-color: red;
}
<div class="graph-container">
<h1>Graph Container</h1>
<div class="graph">Graph</div>
</div>
The parent needs display: grid
and grid-template-rows: auto 1fr
.
grid-template-rows: auto 1fr
makes the second child take up the remaining space that is left over after the other children get their respective space that is defined without the fr
-unit.
Here, <h1>
gets its space it requires, and all the remaining space is given to .graph
.
.graph-container {
display: grid;
grid-template-rows: auto 1fr;
}
/* Ignore; only for displaying the example */
* {
margin: 0;
font-size: 24px;
}
html, body {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.graph-container, .graph {
border: 3px solid black;
box-sizing: border-box;
}
.graph-container {
height: 60vh;
width: 70vw;
}
.graph {
border-color: red;
}
<div class="graph-container">
<h1>Graph Container</h1>
<div class="graph">Graph</div>
</div>
calc()
font-size
to height
As with the solution of sergey kuznetsov, you can guess that height
is approximately equal to font-size
.
But the actual height
depends on line-height
, which depends on the current font.
If line-height
is just 1.05
-times (or 5%) larger than font-size
, the difference is negligible for small values (due to rounding to pixels) but noticable for larger values.
Examples (where floor()
cuts off the decimal places):
floor(20px of 'font-size' * 1.05) = 20px of 'height'
floor(60px of 'font-size' * 1.05) = 63px of 'height'
This means small font-size
values like 20px provide the expected result:
calc(100% - 20px - 3px)
(20px
due to font-size
, 3px
due to border-width
)
h1 {
font-size: 20px;
}
.graph {
height: calc(100% - 20px - 3px);
}
/* Ignore; only for displaying the example */
* {
margin: 0;
font-size: 24px;
}
html, body {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.graph-container, .graph {
border: 3px solid black;
box-sizing: border-box;
}
.graph-container {
height: 60vh;
width: 70vw;
}
.graph {
border-color: red;
}
<div class="graph-container">
<h1>Title</h1>
<div class="graph">Graph</div>
</div>
But large font-size
values like 60px result in visible inaccuracies due to wrongly assuming that font-size
equals line-height
:
calc(100% - 60px - 3px)
(60px
due to font-size
, 3px
due to border-width
)
h1 {
font-size: 60px;
}
.graph {
height: calc(100% - 60px - 3px);
}
/* Ignore; only for displaying the example */
* {
margin: 0;
font-size: 24px;
}
html, body {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.graph-container, .graph {
border: 3px solid black;
box-sizing: border-box;
}
.graph-container {
height: 60vh;
width: 70vw;
}
.graph {
border-color: red;
}
<div class="graph-container">
<h1>Title</h1>
<div class="graph">Graph</div>
</div>
Instead of assumptions, you should use the intended properties to avoid such errors. Define the height
property directly instead of expecting that font-size
and line-height
will equal it.
Similarly, you can define one value to be used for multiple properties with custom properties:
You set the height of the second child to the remaining space with calc(100% - TITLE_HEIGHT)
where TITLE_HEIGHT
is the height of the other child, <h1>
.
We can use CSS Custom properties to set the height to reduce the amount of "magic numbers", which makes refactoring the code later on easier, and perhaps already easier to read.
I used --title-height
for the height.
Custom properties are only accessible in the element itself and its children, which means that we need to define it in their parent .graph-container
.
Now we can use the value of --title-height
by resolving it using var()
like this:
var(--title-height)
.graph-container {
--title-height: 26px;
}
h1 {
height: var(--title-height);
}
.graph {
height: calc(100% - var(--title-height));
}
/* Ignore; only for displaying the example */
* {
margin: 0;
font-size: 24px;
}
html, body {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.graph-container, .graph {
border: 3px solid black;
box-sizing: border-box;
}
.graph-container {
height: 60vh;
width: 70vw;
}
.graph {
border-color: red;
}
<div class="graph-container">
<h1>Graph Container</h1>
<div class="graph">Graph</div>
</div>
When using JS, we need to set the parent's height as well as the height of the <h1>
-element, and calculate the remaining height for .graph
. The remaining height can be set as the .graph
's height using Element.style.height
.
var container = document.querySelector('.graph-container');
var title = document.querySelector('h1');
var graph = document.querySelector('.graph');
var remainingHeight = container.clientHeight - title.clientHeight;
graph.style.height = remainingHeight + "px"; // Make sure it is of unit "px"
/* Ignore; only for displaying the example */
* {
margin: 0;
font-size: 24px;
}
html, body {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.graph-container, .graph {
border: 3px solid black;
box-sizing: border-box;
}
.graph-container {
height: 60vh;
width: 70vw;
}
.graph {
border-color: red;
display: block;
}
<div class="graph-container">
<h1>Graph Container</h1>
<div class="graph">Graph</div>
</div>
The problem with this approach is, that the height will be fixed once found. That will make .graph
not responsive. To achieve that .graph
is responsive, we need to observe if the parent was resized using a ResizeObserver
.
Every time the parent resizes, recalculate the size of .graph
.
var container = document.querySelector('.graph-container');
var title = document.querySelector('h1');
var graph = document.querySelector('.graph');
new ResizeObserver(() => {
graph.style.height = container.clientHeight - title.clientHeight + "px";
}).observe(container);
/* Ignore; only for displaying the example */
* {
margin: 0;
font-size: 24px;
}
html, body {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.graph-container, .graph {
border: 3px solid black;
box-sizing: border-box;
}
.graph-container {
height: 60vh;
width: 70vw;
}
.graph {
border-color: red;
display: block;
}
<div class="graph-container">
<h1>Graph Container</h1>
<div class="graph">Graph</div>
</div>