diff --git a/src/components/Nav.astro b/src/components/Nav.astro index 728b51c..aa82f69 100644 --- a/src/components/Nav.astro +++ b/src/components/Nav.astro @@ -124,6 +124,23 @@ const links = [ height: 60px; } + /* At the top of the page the navigation is empty here: no links, and the + name and the button are still in the hero. Rather than push the hero + down by its height, it floats over it, transparent, until the page + scrolls. Without JavaScript it shows both, so it keeps its place. */ + :global(.js) nav { + margin-bottom: -60px; + transition: + border-color 0.3s, + background 0.3s; + } + + :global(.js) nav:not(.scrolled) { + background: transparent; + backdrop-filter: none; + -webkit-backdrop-filter: none; + } + .logo { font-size: 15px; } diff --git a/tests/site.spec.ts b/tests/site.spec.ts index 45cf8a4..50204d5 100644 --- a/tests/site.spec.ts +++ b/tests/site.spec.ts @@ -222,6 +222,22 @@ test('shows the name and the button in the navigation only once scrolled past', await expect(button).toBeVisible(); }); +test('starts the name near the top on a phone, under a navigation that floats', async ({ + page, +}) => { + await page.setViewportSize({ width: 390, height: 844 }); + // Measured where the name rests, not while it fades in. + await page.emulateMedia({ reducedMotion: 'reduce' }); + await page.goto('/'); + // The empty navigation no longer pushes the hero down: only its own margin. + const top = await page.locator('#top h1').evaluate((h1) => h1.getBoundingClientRect().top); + expect(top).toBeLessThanOrEqual(48); + + // Once scrolled, it has its background again, over the content. + await page.locator('#experience').scrollIntoViewIfNeeded(); + await expect(page.locator('#nav')).not.toHaveCSS('background-color', 'rgba(0, 0, 0, 0)'); +}); + test('counts the visit, and the CV, the thesis, an email and a link out', async ({ page }) => { // A stand-in for GoatCounter's script, which records what it is given. await page.route('https://gc.zgo.at/count.js', (route) =>