Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
4 changes: 2 additions & 2 deletions src/routes/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ let { children } = $props();
<style>
.page { display: flex; flex-direction: column; min-height: 100vh; }
.site-nav { background: var(--bg); border-bottom: 1px solid var(--border); }
.site-nav-inner { display: flex; gap: 24px; max-width: 1200px; padding: 0 24px; margin: 0 auto; }
.site-nav-inner { display: flex; justify-content: center; gap: 24px; max-width: 1200px; padding: 0 24px; margin: 0 auto; }
.site-nav a { padding: 10px 0; color: var(--text-mid); font-size: 13px; text-decoration: none; border-bottom: 2px solid transparent; }
.site-nav a:hover { color: var(--text); }
.site-nav a.active { color: var(--text); font-weight: 600; border-bottom-color: var(--text); }
@media (max-width: 640px) { .site-nav-inner { gap: 16px; padding: 0 16px; overflow-x: auto; } .site-nav a { flex: 0 0 auto; } }
@media (max-width: 640px) { .site-nav-inner { justify-content: flex-start; gap: 16px; padding: 0 16px; overflow-x: auto; } .site-nav a { flex: 0 0 auto; } }
</style>
34 changes: 34 additions & 0 deletions tests/e2e/shell.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
Loading