Home > Back-end >  Make grid container 100% height of content
Make grid container 100% height of content

Time:09-19

Goal: sticky nav bar at top, followed by grid where last row of the grid maintains its natural height, but is at the bottom of the window if there's insufficient content to push it off screen.

Problem: I'm having issues getting my grid to be 100% of the height of the content, which is messing up my sticky nav bar (at least I think this is the issue). My content is coming from a headless CMS, and HTML is generated via NextJS. A page generally looks like this:

<html>
    <body>
        <div id="__next">
            <header></header> // should be sticky
            <div> // wrapper, which I'm making a grid
                Somewhat arbitrary content from the CMS...
                <footer>Comes from the CMS also</footer>
            </div>
        </div>
    </body>
</html>

Styling the header like so basically worked:

header {
  position: sticky;
  top: 0px;
  left: 0px;
  width: 100%;
}

However, when I got around to creating custom error pages, I realised that if there was insufficient content, the footer inside my grid wasn't at the bottom of the page. After some googling and experimenting, the solution seemed to be to make sure that the html and body both had height: 100%; min-height: 100vh;, and that, because there was only 1 row in the grid before the footer, I could do grid-template-rows: 1fr auto;, so the the first row would expand to fill the available space, and the footer would shrink to its proper size, but get pushed to the bottom.

So far, so good — that worked on the pages with little/no content. But, now I have a problem with regular pages (i.e., pages with more than one row, and/or sufficient content to be taller than vh.

As you can see in this snippet, the sticky nav bar is sticky for a while, but then stops being sticky. This appears to me to be because the grid container is not expanding to the height of all of its content.

html {
  height: 100%;
}

body {
  height: 100%;
}

#__next {
  min-height: 100vw;
  height: 100%;
}

header {
  position: sticky;
  top: 0px;
  left: 0px;
  width: 100%;
}

footer {
  padding-left: calc(((100vw - 80rem) / 2)   (80rem / 12));
  padding-right: calc(((100vw - 80rem) / 2)   (80rem / 12));
  width: 100%;
  grid-column-start: 2;
  grid-column-end: 12;
  /* appearance only */
  padding: 6rem 0;
  background-color: #121212;
  color: #fff;
}

.wrapper {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  /* grid-auto-rows: 1fr; */
  max-width: 80rem;
  min-height: 100%;
  height: 100%;
  margin: 0 auto;
}

.item-1 {
  grid-column-start: 2;
  grid-column-end: 7;
  align-self: flex-end;
}

.item-2 {
  grid-column-start: 2;
  grid-column-end: 7;
  align-self: flex-start;
}

.item-3 {
  grid-row: span 2 / span 2;
  grid-row-start: 1;
  grid-column-start: 7;
  grid-column-end: 12;
  height: 787px;
  background-color: #dedede;
}

.item-4 {
  grid-column-start: 2;
  grid-column-end: 12;
}
<html>

<body>
  <div id="__next">
    <header>
      <nav><span>I'm in the header, and should stick. Scroll down to see it work for a while ... and then stop.</span></nav>
    </header>
    <div >
      <div >
        <h1>I'm at the bottom of my container.</h1>
      </div>
      <div >
        <p>I flow nicely after that, because I'm at the top</p>
      </div>
      <div >I'm an image</div>
      <section >
        <p>I'm some more content.</p>
        <div style="height: 300px">Just taking up space</div>
      </section>
      <footer>I'm fancy, I'm a footer.</footer>
    </div>
  </div>
</body>

</html>

I tried grid-auto-rows: 1fr;, grid-template-rows: 1fr auto;, and grid-auto-rows: auto;, but to no avail.

How do I achieve my desired goal (i.e., footer inside grid at bottom of window when insufficient content; sticky nav bar, outside of grid)?

Edit: NB: Since I'm writing the NextJS, and control the CMS templates, I can move the footer out of the grid, if that helps at all.

CodePudding user response:

I can see that structure of your html is a bit messy.

First things first. Make sure you need a sticky navbar. Usually navigation menus are position: fixed. The only reason I can imagine for sticky navbar is if you have 2 separate content bodies and you want to have navbar only for one of them or different navbar for the other.

Here is structure you want.

html, body { margin: 0; padding: 0;}

body {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

header {
  padding: 20px;
  background-color: blue;
  position: sticky;
  top: 0px;
}

.imitation-of-content {
  height: 2000px;
}

.scope-of-sticky-navbar {
  background-color: yellow;
}

.content-without-navbar {
  background-color: green;
}


footer {
  flex: 0 0;
  padding: 20px;
  background-color: red;
}

.scope-of-sticky-navbar, .content-without-navbar {
  flex-grow: 1;
}
<html>

<body>

  <div >
    <header>Header stuff</header>
    <div class='content'>
      <div >content</div>
    </div>
  </div>
  
  <div >
    <div >content2</div>
  </div>
  
  <footer>
    Footer stuff
  </footer>
  
</body>

</html>

position: sticky will just follow scrolling till the end of its parent element. Offset from top and bottom is regulated with top, bottom properties on sticky element and is relative to the parent element.

Avoid setting height of elements manually if their height changes depending on content. height: 100% is making your wrappers height 100% relative to its parent, which messes things up and makes it's children overflow. All you need is min-height. Let it figure out it's actual height on it's own.

  • Related