Stacking-context Trap

Animating the sidebar introduced a stacking order context issue that I didn’t pick up on right away.

The cause is a classic stacking-context trap created by the transform you added to make the slide animation work.

What’s happening:

baseof.html:6 has the splatter as a direct sibling of the header, positioned absolutely with z-10:

<div class="absolute top-[-175px] left-[-150px] z-10">{{ partial "flair/splatter.svg" . }}</div>
<header id="site-header" ...>
  • Inside site-header.html:1-4, the nav content lives in <div class="wrapper relative z-15">. That z-15 is what’s meant to let the sidebar’s content out-rank the splat’s z-10.
  • #site-header itself has no position/z-index — it’s a plain static box. Before you added transform to #site-header[data-state="open"], that meant the header had no stacking context of its own, so its descendants’ z-index (the z-15 wrapper) competed directly against the splat’s z-10 in the same (root) stacking context — and correctly won, painting above the splat.
  • Now that #site-header[data-state="open"] has transform: translateX(0), that’s a non-none transform value, and any transform value other than none creates a new stacking context on that element (this is true even for an identity transform like translateX(0)). That promotes #site-header to act like position: relative; z-index: 0, and traps all its descendants’ z-index values inside that new local context.
  • The inner z-15 div can now only win stacking fights within the header. As a whole unit, the header itself has an effective rank of 0 in the root context — which loses to the splat’s z-10 — so the splat now paints over the entire sidebar, text included.

The Fix

Give #site-header itself an explicit z-index higher than the splat’s z-10 (e.g. z-20), so the whole header — now an atomic stacking unit because of the transform — outranks the splat regardless of what’s happening to its descendants internally:

#site-header {
    z-index: 20; /* or add a `z-20` class in baseof.html */
    transition: opacity 0.3s ease-in-out, display 0.3s ease-in-out, transform 0.3s ease-in-out;
    transition-behavior: allow-discrete;
}

The inner z-15 on the .wrapper no longer matters for beating the splat once the header has its own stacking context — the fight now happens one level up, at the header vs. splat level.