Skip to main content




Created: 2024-Feb-24


Space: The Final Frustration

This story starts with viewing my site on my phone, and noticing that the on-screen buttons covered the bottom of the footer. A bit of margin would be an easy fix. Of course, I wanted to check a couple pages to make sure they were consistently looking good, as I had had unexpected issues in the past with my main page being too short to trigger certain bugs.

Moving to the About page (which, at the time of writing, was roughly 4x the length of the pain page), I noticed it had a bit more height, ending about mid-page. Going to an even longer blog post, there was so much empty space at the bottom that it looked noticably awkward, with the user being able to scroll the footer almost out of sight at the top.

I had a few hunches about what might be causing this, but it was certainly a frustrating problem.

Inspect Everything, Hope-Based Refactoring

I was 99% sure the problem was within my global-mobile-first.css file. There were few other things that could cause this kind of issue. Within that page, I had a few guesses, driven by the fact that the empty space got bigger on longer pages.

The first thing I checked was my grid-template-rows. My initial attempt at creating these rows had me using a unit fr with which I was not familiar, but that I understood to be fractions of a page, and also using em like so:

grid-template-rows: .05fr .95fr 4em;

I figured that I hadn’t reduced the fraction to take into account the amount taken by the em, and that it was rebelling. To my great dismay, reducing the .95fr did nothing to resolve the weird space, and neither did removing the 4em.

The next logical thing to double check was margins and padding using relative units. Now, I use relative units for most of my styles, but my go-to is “em”, and I knew that one didn’t scale on page length. One I had been experimenting more with recently was vh, which stands for Viewport Height, which seemed at first blush to be a likely candidate. After temporarily replacing these with absolute values and continuing to see the error, I started to panic. I was running out of good ideas.

Besides, re-reading documentation had assured me that viewport isn’t related to a page length, but rather to the size of the device screen (the port through which you view the website, as it were).

Just Blame Default Styles

As I had run out of my depth looking for reasonable solutions, I did the only logical thing left: I blamed a higher power. When in doubt, default styles are to blame!

As with most of the frontend domain beyond the depth of basic HTML and CSS, default styles are a relatively new concept to me. I ran into them while applying styles (since removed) to my RSS feed, and wondering why the pretty-feed-v3.xsl I was using had a gargantuan block of CSS inside it, and from there rabbit-holing into css normalization and CSS resets. I wasn’t willing to do a CSS reset for the sake of my site (especially since it still seemed absurd to me that this would be the problem), so I settled for blame.

As usual, blame didn’t solve my problems. Blame is rarely useful in its raw form. The only productive thing blame did for me was resign me to the idea of finding a solution that I knew would be a hack.

Negative margins are conceptually gross, but I’ve used them in a couple places because they do the trick nicely (its even nicer when a subsequent refactor means they can be replaced with something that makes more sense). I would be fine, at least until i found something better, using “larger negative margins on pages with larger ‘default’ bottom margins” if that’s what it took. I just needed a way to detect long pages, and I could cobble something together.

Solve it with Queries

I use media queries for screen width detection, and had it in my head that there should be a way to use an element’s condition within the media query as well. Something along the lines of

@media only screen AND (.my_class > 200vh){
    margin-bottom: -5em;

Reading documentation led me to believe this wouldn’t work, and also led me to the near equivalent: container queries. That seemed like a good solution for about 5 minutes until implementation experiments confirmed some limitations of the “contain” part of a container query; in order to make my main content queryable, it needed to be marked in CSS as a container, and the queryable dimension needed to be specified using container-type. So, for whatever axis I wanted to measure, that axis would also have containment applied to it, which in my case would break vertical scrolling and make my site unusable.

Now, it could have been that if I had kept digging in this direction I would’ve found gold, but at this point it seemed I had run into bedrock, so I went a different direction.

Working with media queries had often raised the question: when should I attempt to do something in Javascript rather than CSS? Without convincing reason, I had decided that everything possible to do in CSS should be done in CSS. I now felt that this problem couldn’t be easily solved with CSS.

Solve it with JS

In javascript I had a solid plan:

  1. Select the element.
  2. Check the height.
  3. Conditionally modify the margin.

It was a Simple Plan, and I had already found scrollHeight and clientHeight, a few promising candidates for properties to check against. I created a new JS file and, knowing that I still had a few steps to figure out, I decided I’d open with a docblock outlining my plan steps. It started:

 * This is a hack.

I think I got about half a setence beyond that before shuddering with revulsion and deciding to dig around in the developer tools looking for clues as to how else I might fix this.

That dig led me to the Layout section once again and reminded me of my grid usage. That plus my (now arguably useful) blame of default styles prompted a simple, brilliant, likely incorrect thought to crossed my mind:

What if there’s an invisible grid space below the footer, and it’s being created by the default styles?

Default Grid Rows?

With this thought in mind I raced my mouse a few hundred pixels right and onto the screen displaying VS Code, intent on taking another stab at grid-template-rows. Are default styles adding an additional grid row? Can I override it?

I tried once:

#inner-body {
    grid-template-rows: .05fr .5fr 4em .45fr;

The thought here was that previously when I had been modifying my .95fr, reducing it was doing nothing because it had nowhere to go. No, that doesn’t really make sense, but I don’t care, because running this test changed the size of the gap. PROGRESS!

Now to incorporate my willingness to use negative margins to get what I want:

#inner-body {
    grid-template-rows: .05fr .5fr 4em -.1fr;

From a bit of trial and error, that is the exact amount I needed for every page to look identical, and have a good amount of bottom margin for mobile viewing as well as desktop viewing.

Now to read the documentation and refactor.

Read the Documentation?

Seems obvious, right? The challenge was that I didn’t know until a couple minutes ago that this was even what was causing the blank space. I had built a mental model of grid that had so far been good enough to get the job done, and had then moved on to other parts of the site that weren’t yet good enough.

Honestly that strategy is fantastic for a lot of things, but it only works well if you’re willing to upend your model when it starts failing you.

So, I had a solution that worked, and I had a hypothesis of why it worked: overridden default style magic. “magic” was my queue I didn’t understand well enough what was going on, and that there was likely a better explanation that didn’t involve magic.

Time to upend the ol’ mental model.

Read The Documentation, Update The Mental Model

Starting at grid-template-rows I did a little bit of reading. The first thing I noticed is that there was an auto value I had never tried. Its description also mentioned something interesting:

Note: auto track sizes (and only auto track sizes) can be stretched by the
align-content and justify-content properties.
Therefore by default,
an auto sized track will take up any remaining space in the grid container.

I plugged it in, and removed the -.1fr hack, and everything worked.

”A little bit” of reading indeed! Documentation is fantastic. I was back to “good enough”.

Good Enough…?

I had a solid solution, and my pages looked great. I was mostly happy with my mental model.

However, I like using grid, and I knew that this mistake might not always be as easily fixed with auto for other grids I might produce. I knew I had to correct my misunderstanding of fr units for this story to have a satisfying conclusion. So, off I went to the documentation for fr.

Looking at it now, it’s so obviously clear I wasn’t handling fractions in the same way the system was. I was using fractional values that added up to 1, whereas the fr examples all use whole numbers and divided the available space by however many total fr there were.

Another thing that I’ve since noticed is that fr doesn’t seem to mean anything if only one of the rows is using that unit. For instance, these three lines produce the same result:

#inner-body {
    grid-template-rows: .05fr auto 4em;

#inner-body2 {
    grid-template-rows: 1fr auto 4em;

#inner-body3 {
    grid-template-rows: 100fr auto 4em;

Good Enough?

Now, that last part still doesn’t feel intuitive to me, but hey, I can live with that, and I’m happy to update my mental model again should the need arise.

Good enough!