One frontend styling issue that has always annoyed me is sticking footers to the bottom of the page when there’s not enough content to force it down.
Sure there are a few ways to do it. You can work with negative margins or calc()
, but that usually requires explicit heights on the footer. And who knows what that will end up being for a WordPress theme.
There’s also a way to do it with flex, but that still requires a few too many rules for my liking. I just want the footer to stick to the bottom!
CSS Grid For The Win!
Do the kids still say “For The Win!”? That’s beside the point. We’re here to make sticky footers with CSS Grid.
CSS Grid allows us to easily place elements on our site on, well, a grid. The best part is we can assign values that allow our elements to automatically take up the space needed to force that footer to the bottom.
The Markup
We’ll take a look at the markup from my starter theme gibby, which is just a fork of _s. But it’s got simple markup with one tiny little trick.
Here’s the relevant markup.
<div id="page" class="site">
<a class="skip-link screen-reader-text" href="#content">Skip to content</a>
<header id="masthead" class="site-header"></header>
<div id="content" class="site-content"></div>
<footer id="colophon" class="site-footer"></div>
</div>
It’s a simple layout. We’ve got the #page
element wrapping the site.
Then inside we have four elements. An <a>
element for screenreaders. A <header>
, #content
for the main content, and a <footer>
.
In our case we want to make the <header>
and <footer>
take up as much space as they need, and have the #content
fill in any leftover space to make the <footer>
sticky to the bottom of the page.
The CSS
This is where CSS Grid comes into play. Let’s jump into the CSS then break it down.
.site {
display: grid;
grid-template-columns: 100%;
grid-template-rows: auto 1fr auto;
min-height: 100vh;
}
That’s it. 3 rules on a single class and we’ve got a footer stuck to the bottom of the page even when we have short content.
Rule number 1, display: grid;
sets our .site
wrapper element up as grid.
Then we tell our grid how many rows to consist of and the size of each row with grid-template-rows: auto 1fr auto;
.
If you’re paying attention you’ll notice that only comes to 3 rows, but we had 4 elements in our .site
element.
That’s the aforementioned tiny little trick. Since the screenreader <a>
element is positioned absolutely it doesn’t place in our first grid row.
So our remaining 3 elements place themselves in our defined grid rows.
The <header>
element goes in the first row which has a height of auto
. So it will take up as much space as it needs. Jumping ahead our third row gets the <footer>
which also has an auto
height.
Then the #content
element goes into the second row which uses the new fractional unit in grid to take up any space not taken up by the other two elements.
We wrap it up with a min-height: 100vh
that makes the .site
element at least 100 percent of the viewport height.
Update: I recently ran into an issue when using a <pre>
element that caused the grid to expand past 100% of the screen width causing horizontal scrolling on small screens. I was able to fix this by setting a single grid column explicitly to 100% width. The new rule added was grid-template-columns: 100%;
.
Essentially that tells our grid to be a single column that’s 100% of the width of the site. As a side note using 1fr
here did not fix the issue.
Wrapping Up
At this point CSS Grid works in all modern browsers. I don’t consider a non-sticky footer to be user experience breaking, so you’re fine to go forth without a fallback. Of course if you really feel you need to make a fallback for those older browsers *cough* IE *cough* go forth and conquer.
I’m a fan of the CSS Grid option for it’s simplicity and the fact that everything has the ability to grow. Which is essential when building WordPress themes that can be used in any number of ways.