Cross-Site Navigation
Learning Website: Framework & Architecture
Chapter 5 · Navigation Across Sites
On a single site, navigation is easy to take for granted: every page is one link away from every other. Once the content is spread over several sites, a visitor needs three things to keep their bearings: a menu for the part of the site they are in, a way to see and reach the other sites, and links inside lessons that still work when they point across a boundary. This chapter starts from how your current navigation is really built, then designs the three layers for the multi-site version.
How Navigation Works Today
The live Astro site has four navigation pieces, and two of them are derived automatically while one is typed by hand:
| Piece | Source | Maintained by |
|---|---|---|
| Top menu (dropdown groups) | NAV_CATEGORIES and STANDALONE_SLUGS in nav-groups.ts | By hand |
| Home page subject grid | Every top-level folder of the content tree | Automatic |
| Sidebar tree | The folder tree of the current subject, drilled to the active page | Automatic |
| Breadcrumb and page titles | The folder path, with the chapter title read from the banner | Automatic |
The site tree is built by walking the content/ folder, and the site is configured with
trailingSlash: 'always', so every directory URL ends in /. Both facts matter for
what follows.
blockchain-and-web3, classic-literature, cookery,
culture, football, ios-development, music,
philosophy, politics, sci-fi-project, science,
science-fiction and study-methodologies. They are still reachable from the home
page grid and the course index, so nothing is lost, but the menu does not show them. The file says itself that
a folder left out simply does not appear. A list that is maintained by hand and has no check will drift.
Three Layers for Several Sites
With separate sites, one menu cannot do every job. Split navigation into three layers, each with a different scope and a different source:
| Layer | Shows | Same on every site? | Source |
|---|---|---|---|
| 1. Global bar | The family of sites, with the current one highlighted | Yes | The site map (one file) |
| 2. Site menu | The subjects within this site | No: one per site | Derived from the site's own folders |
| 3. Page navigation | The sidebar tree, breadcrumb and chapter previous/next | No: depends on the page | Derived from the folder tree |
Layer 1: the global bar
A thin bar at the top of every page lists the sites: Languages, Web Development, Programming, Systems and so
on, with the current one marked. It is the same markup everywhere, generated from the site map, so adding a
site means adding one entry. Eight short names fit comfortably on a desktop. On a narrow screen, collapse
them into a <details> dropdown, the same no-JavaScript technique the current menu already
uses.
The first item links back to the root domain, which Chapter 1 suggested becoming a portfolio and landing
page. Note that the current Astro configuration uses https://www.osztromok.com as the site
address. Decide early whether the landing page lives on www, on the bare domain, or on both
with a redirect, because every global-bar link and every canonical URL depends on it.
Layer 2: the site menu
Each site's menu lists only its own subject folders, taken from the map and the folder tree. Because it is
generated, it cannot drift the way the current list did. A site gets a Sidebar entry only if some
sidebar/<subject>/ folder is routed to it (Learning Website: Framework & Architecture
2). Exercise 3 builds this and shows the result for two sites.
Layer 3: page navigation
The sidebar tree and the breadcrumb already work from the folder structure, so they carry over unchanged, with one addition: the first breadcrumb item becomes the site, linking to its home, optionally preceded by the family root. Previous and next links between chapters use the chapter number, sorted as a number (Learning Website: Framework & Architecture 3).
Links Inside Content
The hardest part is not the menus. It is the thousands of links written inside lessons, such as
/linux/shell-and-scripting/vim/, that assume one site. After a split a link may stay on the
same site or cross to another one. Two rules keep this manageable:
- Authors keep writing root-relative paths. Nobody has to remember which site a topic lives on.
- The build resolves them. A resolver looks up the target's site using the same function as Chapter 2. A same-site link stays relative. A cross-site link becomes an absolute URL on the other site.
This has three benefits. A folder that later moves to another site needs one line changed in the map, not
thousands of edits. Local development can use another base address (such as *.localhost) from
the same table. And a link to a path that belongs to no site fails loudly during the build instead of
shipping broken. Exercise 2 writes this resolver and runs it on nine kinds of link.
/resources/japanese/kanji/, which is not a folder under
content/. The resolver needs an explicit entry for that prefix. Collect these exceptions
deliberately: Learning Website: Framework & Architecture 11 audits every link and will find the rest.
Opening Cross-Site Links
- Keep the visitor in context. A cross-site link in a lesson should usually open in the same tab, so the browser's Back button works. Reserve a new tab for genuinely external sites.
- Mark them. A small marker or label such as “Systems site” tells visitors a link leaves the current site, so a change of menu is no surprise.
- Mind the origin. A normal link is fine across origins. What does not work across subdomains is shared
localStorageand, without CORS, scripted requests (Learning Website: Framework & Architecture 1).
Keeping Navigation Honest
Navigation fails quietly. A missing menu entry or a link to the wrong site does not cause an error; a visitor just cannot find something. Add checks to the build:
- Every folder is assigned to exactly one site (Chapter 2's checker).
- Every site menu entry points at a folder that exists.
- Every internal link in every page resolves to a known site and, after the build, to a file that exists.
data-science-and-ml comes out as “Data Science And Ml” and
cloud-and-devops as “Cloud And Devops”, because the live label helper has no
ml in its acronym list and does not lower-case small words. Fix both once in the shared helper
rather than renaming folders.
Hands-On Exercises
Write nav_gap.py, which reads the slugs in nav-groups.ts and compares them with the real top-level folders of content/. Report the folders with no menu entry and any menu entry that points at nothing, and explain why a list like this drifts.
Write link_for(current_site, href), which keeps same-site links relative, turns cross-site links into absolute URLs, and leaves external links, anchors and relative paths alone. Keep the path, query, fragment and trailing slash, make the base address configurable per environment, and test it on at least eight links.
Write nav_for(site, content), which returns a site's menu from the site map and the real folders with readable labels. Add a Sidebar entry only for sites that receive sidebar pages, run it for two sites, and note any label that comes out badly.
Chapter 5 Quick Reference
- Today: the top menu is hand-written; the home grid, sidebar tree and breadcrumbs are derived from the folder tree
- The hand-written menu has drifted: 13 of 49 folders are missing from it (still reachable from the home grid and the course index)
- Three layers: global bar (all sites), site menu (this site's subjects), page navigation (tree, breadcrumb, previous/next)
- Generate menus from the site map and folders so they cannot drift
- Authors write root-relative links; the build resolves them with
site_for() - Same-site links stay relative; cross-site links become absolute URLs; anchors, external and relative links are untouched
- Keep the trailing slash (
trailingSlash: 'always') when rebuilding links - Some URLs are not folder names (the kanji resources): list them explicitly
- Decide the landing page address (
www, bare domain or both) early - Add build checks: every folder assigned, every menu entry valid, every internal link resolvable