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:
CSS Flexbox
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
.
/* 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;
}
/* The answer */
.graph-container {
height: 60vh;
width: 70vw;
display: flex;
flex-flow: column; /* Shorthand for ('flex-direction' | 'flex-wrap') */
}
.graph {
flex-grow: 1;
border-color: red;
}
<div class="graph-container">
<h1>Graph Container</h1>
<div class="graph">Graph</div>
</div>
CSS Grid
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
.
/* 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;
}
/* The answer */
.graph-container {
height: 60vh;
width: 70vw;
display: grid;
grid-template-rows: auto 1fr;
}
.graph {
border-color: red;
}
<div class="graph-container">
<h1>Graph Container</h1>
<div class="graph">Graph</div>
</div>
CSS calc()
Relation of font-size
to height
As with the solution of sergey kuznetsov, you can guess that height
is (for small sizes only!) equal to font-size
.
This is not always true, because height
actually depends on line-height
, which again depends on the font currently used.
The problem, illustrated
Say for example, that line-height
is about 1.05
-times taller than font-size
for a given font. Since the browser just can't display less than a px
-unit, the decimal places are effectively cut off.
That makes the equation only true for small values of font-size
, as can be seen in the examples below.
Examples (where toInt()
cuts off the decimal places):
toInt(20px of 'font-size' * 1.05) = 20px of 'height'
toInt(60px of 'font-size' * 1.05) = 63px of 'height'
This means, while this works (for small font-size
-values):
calc(100% - 20px - 3px)
(20px
due to font-size
, 3px
due to border-width
)
/* 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;
}
/* The answer */
.graph-container {
height: 60vh;
width: 70vw;
}
h1 {
font-size: 20px;
}
.graph {
height: calc(100% - 20px - 3px);
display: block;
border-color: red;
}
<div class="graph-container">
<h1>Title</h1>
<div class="graph">Graph</div>
</div>
... this won't work (for great font-size
-values):
calc(100% - 60px - 3px)
(60px
due to font-size
, 3px
due to border-width
)
/* 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;
}
/* The answer */
.graph-container {
height: 60vh;
width: 70vw;
}
h1 {
font-size: 60px;
}
.graph {
height: calc(100% - 60px - 3px);
display: block;
border-color: red;
}
<div class="graph-container">
<h1>Title</h1>
<div class="graph">Graph</div>
</div>
That is why it is best to actually define the height
-property itself instead of relying on font-size
or line-height
to be the same as height
(which it apparently isn't always). Which brings us to...
Using CSS 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)
/* 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;
}
/* The answer */
.graph-container {
--title-height: 26px;
height: 60vh;
width: 70vw;
display: block;
}
h1 {
height: var(--title-height);
}
.graph {
height: calc(100% - var(--title-height));
display: block;
border-color: red;
}
<div class="graph-container">
<h1>Graph Container</h1>
<div class="graph">Graph</div>
</div>
JavaScript
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
.
/* The answer */
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
.
/* The answer */
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>