Index
- Space: The Final Frustration
- Inspect Everything, Hope-Based Refactoring
- Just Blame Default Styles
- Solve it with Queries
- Solve it with JS
- Default Grid Rows?
- Read the Documentation?
- Read the Documentation, Update the Mental Model
- Good Enough…?
- Good Enough?
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 fr
actions 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:
- Select the element.
- Check the height.
- 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!