CSS Grid Gap Behavior with Hidden Elements
I was recently prototyping a component layout that included a way to toggle the visibility of sibling elements inside a grid display. What tripped me up was, while these elements were hidden, all of the container's gap
gutters remained, leaving undesired extra visual spacing. I expected these gutters to collapse. The reason they stick around is related to explicitly defining grid templates.
Template or auto layout?
What are the differences between grid-template-*
and grid-auto-*
when declared for columns or rows in a grid layout? Ire Aderinokun has a fantastic article that thoroughly explains these distinctions and I recommend giving it a read. I'll try to quickly summarize: grid-template-*
sets explicit column and row tracks, while grid-auto-*
creates implicit track patterns.
The following excerpt in the "How grid-auto works" section from the article stood out to me:
Unlike the
grid-template-*
properties, thegrid-auto-*
properties only accept a single length value.
After some experimentation and confirming through examples from the Syntax section in the grid-auto-rows
MDN web docs, I found that multiple track-size values can be used as well. Let's try an example to create a layout commonly referred to as the pancake stack. Its value of auto 1fr auto
will either:
- explicitly size and position only the first three rows when used in
grid-template-rows
- act as a pattern to implicitly size each group of three rows in
grid-auto-rows
Visualize the gap
In the CodePen demo below, tick on the "Hide elements" checkbox to assign display: none
on all but the first two elements in both grid containers.
So what's happening here? When collapsed, the grid-template-rows
container is slightly taller than its grid-auto-rows
counterpart because of the extra space appearing beneath the remaining visible elements. Recall that rows are explicitly set with grid-template-rows
. In this situation, the gap
gutters still apply even when elements are hidden or removed from the container.
I ended up moving forward with grid-auto-rows
for my component's layout needs. You can see a stripped down version of it in the CodePen below. The classic small screen navigation!
A template solution
If using grid-template-*
is preferred or necessary, the solution is to override the property value to match the expected visual result. The above demo could even get by on a single ruleset that applies the template only when the menu is open:
.nav.is-open {
grid-template-rows: auto 1fr auto;
}
This same solution can also work for grid-template-areas
. While it leads to writing more code, it self-documents really nicely.
.nav {
grid-template-areas: "logo toggle";
}
.nav.is-open {
grid-template-areas:
"logo toggle"
"menu menu"
"cta cta";
}
.nav .logo {
grid-area: logo;
}
.nav .toggle {
grid-area: toggle;
}
.nav .menu {
grid-area: menu;
}
.nav .cta {
grid-area: cta;
}