Update. Content can be both higher and wider than container
The "intermediate flexbox" solution provides automatic switching between center and edge alignment of content, but only in one direction - either horizontally or vertically. Therefore, I propose to choose which centering to keep and which to sacrifice.
For example, in this solution, the narrow content is centered and the wide content starts from the left edge of the container. In this case, the content will start from the top edge, regardless of its height. For clarity, I have included four examples with different content sizes in the code.
(The "vertical centered" solution is below in the first version of the answer.)
https://jsfiddle.net/glebkema/8yqxcm9e/
.container {
border: 2px solid red;
display: flex;
height: 250px;
overflow: auto;
}
.intermediate {
display: flex;
flex-grow: 1;
justify-content: center;
}
.content {
border: 2px solid blue;
height: 150px;
margin: 12px;
width: 150px;
}
.content.high {
height: 600px;
}
.content.wide {
width: 900px;
}
@media (min-width: 500px) {
.grid {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
}
}
body {
margin: 0;
}
<div class="grid">
<div class="container">
<div class="intermediate">
<div class="content">Narrow content is centered horizontally</div>
</div>
</div>
<div class="container">
<div class="intermediate">
<div class="content wide">Narrow content is centered horizontally</div>
</div>
</div>
<div class="container">
<div class="intermediate">
<div class="content high">Wide content starts from the left edge</div>
</div>
</div>
<div class="container">
<div class="intermediate">
<div class="content wide high">Wide content starts from the left edge</div>
</div>
</div>
</div>
1st answer. Content can be higher but not wider than container
I've understood the desired behavior like this:
- If the child box is small, it should be centered on the parent.
- If the child box is large, then it should not be cut off at the top, but start from the top edge of the parent.
- At the same time, the sizes of the blocks are not known in advance and their behavior must be adjusted to the sizes of each other.
Problem:
If we just use the justify-content: center; property, and the height of the child block is greater than the height of the parent, then the top part of the child block is cut off and not available for vertical scrolling.
Solution:
Use a hierarchy of three nested blocks too.
But make the outer container a flex block with a column direction, and give it the overflow-y: scroll; property.
And the intermediate block remains a flex block with the justify-content: center; and flex-grow: 1; properties.
.container.scroll {
display: flex;
flex-direction: column;
overflow-y: scroll;
}
.intermediate {
display: flex;
flex-direction: column;
flex-grow: 1;
justify-content: center;
}
So if the child block is smaller than the parent block, it is centered.
And if its height is greater than the parent's height, it starts from the parent's top edge.
Notice:
In the source code, the .app and .header blocks have a height in pixels, and .container has a height of 100%. Thus, it goes beyond the .app block.
To prevent this, I've used flex-boxes for the entire layout too. I also changed the header's height property to min-height to prevent it from shrinking as the container grows.
And here are demos for similar tasks:
.app {
height: 400px;
display: flex;
flex-direction: column;
}
.header {
min-height: 44px;
background-color: grey;
border: 2px solid;
}
.container {
border: 1px solid red;
flex-grow: 1;
}
.container.scroll {
display: flex;
flex-direction: column;
overflow-y: scroll;
}
.intermediate {
display: flex;
flex-direction: column;
flex-grow: 1;
justify-content: center;
align-items: center;
}
.content {
height: 600px;
width: 300px;
margin: 12px;
border: 2px solid blue;
}
.content.small {
height: 250px;
}
.app + .app {
margin-top: 50px;
}
<div class="app">
<div class="header"></div>
<div class="container scroll">
<div class="intermediate">
<div class="content">Large content starts from the container's top edge</div>
</div>
</div>
</div>
<div class="app">
<div class="header"></div>
<div class="container scroll">
<div class="intermediate">
<div class="content small">Small content is centered</div>
</div>
</div>
</div>