diff --git a/PLAN.md b/PLAN.md index 01edc1d..93df4e3 100644 --- a/PLAN.md +++ b/PLAN.md @@ -122,6 +122,7 @@ All package manifests, lockfiles, configuration, documentation, licences, CI, an | 8 | GitHub Pages and domain preparation | Produce strict static output for every included route and asset, add a pinned least-privilege Pages workflow for protected `main`, and prepare domain-root metadata for `link42.app`. Repository Pages settings, custom-domain APIs, DNS, DigitalOcean, and production verification remain excluded. | 7 | done | | 9 | Faithful public homepage migration | Correct the public homepage and shell to match the pinned public-facing source: original copy, section order, visual structure, theme behaviour, and public header/footer presentation. Keep only public surfaces; exclude auth, sessions, API routes, and private runtime/deployment code. | 1, 2, 4, 5 | done | | 10 | Open-source hosting and feedback guidance | Publish a dedicated page covering repository and site hosting, the actual technology used by the website and Rule1, public bug and suggestion routes, and private security reporting; align site navigation and verification with the new route. | 9 | done | +| 11 | Secondary navigation alignment | Centre the secondary navigation link group on desktop and tablet while retaining left-aligned horizontal scrolling and first-link reachability on mobile. | 10 | done | One completed feature equals one tested commit and push. Do not start the next feature with uncommitted work. diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 9660669..84a42e3 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -30,9 +30,9 @@ let { children } = $props(); diff --git a/tests/e2e/shell.spec.ts b/tests/e2e/shell.spec.ts index 591257e..7843404 100644 --- a/tests/e2e/shell.spec.ts +++ b/tests/e2e/shell.spec.ts @@ -65,6 +65,40 @@ test("theme switch is labelled and persists", async ({ page }, testInfo) => { expect(accessibility.violations).toEqual([]); }); +test("secondary navigation is centred on desktop and reachable on mobile", async ({ page }, testInfo) => { + const isMobile = testInfo.project.name.startsWith("mobile-"); + if (isMobile) { + await page.setViewportSize({ width: 320, height: 812 }); + } + + await page.goto("/about"); + const navigation = page.getByRole("navigation", { name: "Site navigation" }); + const navigationInner = navigation.locator(".site-nav-inner"); + const links = navigation.getByRole("link"); + + if (isMobile) { + const firstLink = links.first(); + const firstLinkBox = await firstLink.boundingBox(); + expect(firstLinkBox).not.toBeNull(); + expect(firstLinkBox?.x).toBeCloseTo(16, 0); + await expect(navigationInner).toHaveCSS("justify-content", "flex-start"); + await expect(navigationInner).toHaveCSS("overflow-x", "auto"); + expect(await navigationInner.evaluate((element) => element.scrollWidth)).toBeGreaterThan( + await navigationInner.evaluate((element) => element.clientWidth), + ); + } else { + const firstLinkBox = await links.first().boundingBox(); + const lastLinkBox = await links.last().boundingBox(); + expect(firstLinkBox).not.toBeNull(); + expect(lastLinkBox).not.toBeNull(); + const groupCentre = ((firstLinkBox?.x ?? 0) + (lastLinkBox?.x ?? 0) + (lastLinkBox?.width ?? 0)) / 2; + expect(groupCentre).toBeCloseTo(640, 0); + await expect(navigationInner).toHaveCSS("justify-content", "center"); + } + + await expectNoHorizontalOverflow(page); +}); + test("skip link reaches main content", async ({ page }) => { await page.goto("/"); await page.keyboard.press("Tab");