2

I have a CSS grid with a row div for a header, a row div for content, and a row div for a footer. I want the content to fill the screen but keep the header and footer visible. I have a fixed-size div inside another div inside the content div. Everything except the fixed-size div is height: 100%.

If I apply overflow-y: auto to the content div, the scrollbar appears on the content div. This is great, but what I really want is for the scrollbar to appear on the div inside the content div instead.

Scrollbar on content div

https://jsfiddle.net/efth2akr/2/

If I apply overflow-y: auto to the div inside the content div instead, there is no scrollbar and the content div takes on the height of the fixed-size div. This pushes the footer down and puts a scrollbar on the whole page. What?? Isn't height: 100% supposed to be based on the parent height? Other questions that describe similar scenarios fail to put height: 100% all the way up the chain, but I haven't.

Scrollbar on whole page instead of inner div

https://jsfiddle.net/t08u9wnk/2/

How can I achieve my desired behavior of having the scrollbar appear on the div inside the content div while maintaining a responsive layout? What am I not understanding about height: 100% in this scenario?

Browser: Microsoft Edge 103.0.1264.62

Marco van Hilst
  • 579
  • 5
  • 11

2 Answers2

1

I think the approach you are trying to follow would work only if you set a fixed height or max-height to div.grid-content > div. Now the problem is that the height of this container is dynamic and depends on height: 100%, so you might be tempted to do max-height: 100% but here is a good reason for why this is not possible.

If you want div.grid-content > div to have a scroll and keep the layout of the entire page based on the height of the screen, I'd propose you to use an absolute positioned overlay.

Here is a modified snippet of your code explaining how it works:

html, body {
  height: 100%;
  width: 100%;
}

body {
  /* prevents scroll on the entire page */
  overflow: hidden;
}

*, *::before, *::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

div.grid {
  display: grid;
  grid-template-rows: auto 1fr auto;
  grid-template-columns: 1fr;
  height: 100%;
  width: 100%;
}

div.grid-header, div.grid-footer {
  background-color: #ff0000;
}

div.grid-content {
  background-color: #00ff00;
  /* set relative positioning to contain absolute child */
  position: relative;
}

div.grid-content > div {
  /* this is strategy for creating a container that adapts to the size of its relative parent using absolute positioning without the need of setting a fixed height to div.grid-content */
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  
  /* add scroll on y axis */
  overflow-y: auto;
}

div.grid-content > div > div {
  height: 200vh;
  width: 50%;
  background-color: #0000ff;
}
<div class="grid">
  <div class="grid-header">
    <h3>
      hi
    </h3>
  </div>
  <div class="grid-content">
    <div>
      <div>
        asdf
      </div>
    </div>
  </div>
  <div class="grid-footer">
    <h3>
      bye
    </h3>
  </div>
</div>
Mauro Aguilar
  • 1,093
  • 13
  • 22
  • Wow, thank you! That solves my problem, but it seems like a hack. Could you explain why my approach won't work? – Marco van Hilst Aug 01 '22 at 20:11
  • @MarcovanHilst it won't work because the scroll area needs some sort of fixed height, take a look at the first version of your fiddle and change `height: 100%` to `height: 100px` in `div.grid-content > div` selector and you'll notice how the constrained height will force your `div` to display the scroll. – Mauro Aguilar Aug 01 '22 at 20:44
  • 1
    Because I used `height: 100%` all the way up the chain, the scroll area should have a fixed height. And it does... except when `1fr` is actually `auto` when `auto` is bigger than `1fr`!! See my answer. – Marco van Hilst Aug 01 '22 at 20:55
1

I have been bitten by this unintuitive design choice of CSS grid: https://github.com/w3c/csswg-drafts/issues/1777

In summary, CSS grid "1fr" behaves like minmax(auto, 1fr) (where 1fr is the actual fractional dimension of the grid). The minimum width/height of a "1fr" grid row/column is the auto width/height. If the auto width/height is larger than 1fr, the whole row/column will expand out past 1fr because minmax ignores the max when min > max!

This is why my problem has the same symptoms as all the other questions about height: 100% not working where the answer was that they had an ancestor with height: auto. It turns out 1fr can become auto!

Replacing:

grid-template-rows: auto 1fr auto;

with:

grid-template-rows: auto minmax(0, 1fr) auto;

fixes the problem.

https://jsfiddle.net/t08u9wnk/3/

Marco van Hilst
  • 579
  • 5
  • 11