Commit41a1450unknown_key
Fix sticky-nav CSS bleeding into every content <header> (pricing hero overlap)
Fix sticky-nav CSS bleeding into every content <header> (pricing hero overlap) The layout's nav styles targeted the bare header element — sticky, height: var(--header-h), blur backdrop, border. Every semantic <header> in page content (~200 across src/routes) inherited a 60px sticky bar. On /pricing this crushed the hero to 105px and rendered the headline on top of the plan cards. - Scope the nav rules to a new .site-header class on the layout's own header element (no JS or e2e selectors referenced bare header). - Fix undefined var(--space-7) in .pl-compare (resolved to 0 margin); use --s-14. - Add a regression test asserting the nav styles stay scoped and no bare header selector ships in the page CSS. Verified by rendering /pricing, /, /explore, /changelog, /vs-github, /login headless at 1180/1440/390px in light + dark themes: hero no longer overlaps cards, nav remains sticky after scroll. https://claude.ai/code/session_01BCaibgckcvsuGmByyn8d8L
3 files changed+25−641a145007fc847e77a288d9b988afe9305007a86
3 changed files+25−6
Modifiedsrc/__tests__/pricing-page.test.ts+16−0View fileUnifiedSplit
@@ -101,4 +101,20 @@ describe("L8 — /pricing public page", () => {
101101 expect(body).toContain("Bundle math vs GitHub");
102102 expect(body).toContain("$89");
103103 });
104
105 it("layout nav styles are scoped to .site-header, never bare <header>", async () => {
106 // Regression — 2026-06-10: layout.tsx styled the bare `header` element
107 // (sticky, height: var(--header-h), blur). Every page that uses a
108 // semantic <header> for a hero or section head (~200 across src/routes)
109 // inherited a 60px sticky bar, which crushed the /pricing hero and
110 // overlaid its title on top of the plan cards. The nav rule must stay
111 // scoped to .site-header.
112 const res = await app.request("/pricing");
113 const body = await res.text();
114 expect(body).toContain('<header class="site-header">');
115 // No CSS rule may target the bare element: `header {` or a
116 // `... header {` descendant/`theme header` selector outside a class.
117 const bareHeaderSelector = /(^|[}\s])header(\s+nav)?\s*\{/m;
118 expect(bareHeaderSelector.test(body)).toBe(false);
119 });
104120});
Modifiedsrc/routes/pricing.tsx+1−1View fileUnifiedSplit
@@ -670,7 +670,7 @@ const pricingCss = `
670670 value proposition unmissable. Same visual rhythm as .pl-host-grid; the
671671 "us" side wins via accent border + subtle gradient glow. */
672672 .pl-compare {
673 margin-top: var(--space-7);
673 margin-top: var(--s-14);
674674 }
675675 .pl-compare-grid {
676676 display: grid;
Modifiedsrc/views/layout.tsx+8−5View fileUnifiedSplit
@@ -162,7 +162,7 @@ export const Layout: FC<
162162 />
163163 </div>
164164 )}
165 <header>
165 <header class="site-header">
166166 <nav>
167167 <a href="/" class="logo">
168168 gluecron
@@ -1490,8 +1490,11 @@ const css = `
14901490 }
14911491 .playground-banner-dismiss:hover { color: var(--text-strong, #e6edf3); }
14921492
1493 /* Header — sticky, blurred, hairline border, taller for breathing room */
1494 header {
1493 /* Site nav header — sticky, blurred, hairline border. Scoped to
1494 .site-header: pages use semantic <header> elements for section/hero
1495 headings, and a bare element selector here turns every one of them
1496 into a 64px sticky bar (the /pricing hero-overlap bug). */
1497 .site-header {
14951498 position: sticky;
14961499 top: 0;
14971500 z-index: 100;
@@ -1502,9 +1505,9 @@ const css = `
15021505 backdrop-filter: saturate(180%) blur(18px);
15031506 -webkit-backdrop-filter: saturate(180%) blur(18px);
15041507 }
1505 :root[data-theme='light'] header { background: rgba(251,251,252,0.78); }
1508 :root[data-theme='light'] .site-header { background: rgba(251,251,252,0.78); }
15061509
1507 header nav {
1510 .site-header nav {
15081511 display: flex;
15091512 align-items: center;
15101513 gap: 18px;
15111514