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:

PieceSourceMaintained by
Top menu (dropdown groups)NAV_CATEGORIES and STANDALONE_SLUGS in nav-groups.tsBy hand
Home page subject gridEvery top-level folder of the content treeAutomatic
Sidebar treeThe folder tree of the current subject, drilled to the active pageAutomatic
Breadcrumb and page titlesThe folder path, with the chapter title read from the bannerAutomatic

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.

The hand-written menu has drifted
Exercise 1 compares the menu list with the real folders. Of 49 top-level folders, 13 have no menu entry: 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:

LayerShowsSame on every site?Source
1. Global barThe family of sites, with the current one highlightedYesThe site map (one file)
2. Site menuThe subjects within this siteNo: one per siteDerived from the site's own folders
3. Page navigationThe sidebar tree, breadcrumb and chapter previous/nextNo: depends on the pageDerived 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.

languages.osztromok.com ├── Global bar: Languages* | Web Development | Programming | Systems | AI | ... ├── Site menu: France Germany Hungary Japan Culture └── Page: sidebar tree / breadcrumb / previous - next systems.osztromok.com ├── Global bar: Languages | ... | Systems* | ... ├── Site menu: Linux Operating Systems Networking ... Sidebar └── Page: sidebar tree / breadcrumb / previous - next

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:

  1. Authors keep writing root-relative paths. Nobody has to remember which site a topic lives on.
  2. 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.
# written in a Hungarian chapter, built on languages.osztromok.com /japan/hiragana-1/ -> /japan/hiragana-1/ (same site, stays relative) /linux/shell-and-scripting/vim/ -> https://systems.osztromok.com/linux/shell-and-scripting/vim/ #top -> #top (anchors are never touched) https://example.org/page/ -> https://example.org/page/ (external)

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.

Some URLs are not folder names
The kanji pages are served from /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 localStorage and, 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.
Small things worth fixing on the way
The label for 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

Exercise 1

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.

📄 View solution
Exercise 2

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.

📄 View solution
Exercise 3

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.

📄 View solution

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