Skip to content

feat(masthead): navigation built from the documentation manifests - #112

Open
oto-macenauer-absa wants to merge 1 commit into
masterfrom
feat/masthead-nav
Open

oto-macenauer-absa wants to merge 1 commit into
masterfrom
feat/masthead-nav

Conversation

@oto-macenauer-absa

@oto-macenauer-absa oto-macenauer-absa commented Sep 29, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #111

masthead-wide

What

The masthead navigation is scoped to the app being viewed, built from its kb-docs.json pages:

Library   📖 Guide Mirror  ›  Showcase   Overview   Guide ▾
  • A page without a section is a link of its own; a section is one dropdown of its pages, placed where its first page falls in order. Adding a page to a manifest adds it to the menu; no knowledge base change.
  • On the catalog the bar holds only Library — the catalog is the way between apps, so the bar does not grow with the registry.
  • An app without a pages manifest (crawled, single-page, iframe) shows just its name.
  • ≥ 1024px — full bar; wraps onto a second row rather than scrolling or clipping. Section dropdowns are <details name> (exclusive).
  • < 1024px (tablet first) — compact bar: Library, current app (ellipsised), and for an app with manifest pages a Menu button opening a popover side sheet of that app's pages, sections as headed groups.
  • A11y — current page aria-current="page", the app and section holding it aria-current="true"; visible focus ring; everything keyboard-operable. No-JS core: <details> and the Popover API bring toggling, Escape and light dismiss. src/scripts/masthead-nav.js adds Escape / outside click / tab-out / right-edge flip for the bar dropdowns.

How

  • src/utils/navigation.js — nav model built once per resolved registry (navigationFor, memoised), never passed through getStaticPaths props (keeps the Performance: sub-app assets are copied twice and the whole registry is duplicated into every page's props #51 memory fix). Each app carries items: page and section entries in manifest order. pageRelDir() / routeHref() shared with apps.js, so a menu link cannot point where no route was built.
  • Masthead.astro + NavPages.astro — both renderings of the current app's entries; CSS switches at 1024px.

Brand wordmark

On app pages the "Knowledge base" wordmark was a flex item beside the centred 72rem column, which took its width out of the column and moved every link (Library at x=508 instead of the catalog's 424 at 1920px). It is now out of flow in the left gutter, shown from 90rem where the gutter holds it, so the links sit exactly where they do on the catalog at every width.

Fixture

tests/fixtures/docs-example.kb-docs.tar.gz gains a third app, handbook (same page tree as guide-mirror), with a two-section manifest — Showcase | Getting started ▾ | Authoring ▾ — so exclusive dropdowns are exercised and the feature is visible in npm run build:local && npm run preview. user-guide stays crawled and guide-mirror unchanged. Repacked with actions/lib/pack.js (deterministic); 4.2 → 5.7 MB.

Found along the way

  • Astro 5 renders a component <script> in place, which put it between the masthead and the sub-app content and broke the "next sibling is the app" contract (css-isolation failures). The script now sits inside the masthead.
  • Embedded, reframed reports its iframe's document as the masthead's ownerDocument and getRootNode(), so an outside-click listener there never saw host clicks. The script takes the real window from the opening click's event.view.

Behaviour changes

  • Library / current app are now links marked current instead of inert text; masthead assertions in build-integrity.spec.js rewritten accordingly. Three standalone tests that clicked "the first internal link" (now Library) target a catalog card.
  • Other apps are no longer listed in the masthead; the catalog is the switcher.
  • contract/ARTIFACT.md documents section as "one masthead dropdown".

Tests

  • tests/navigation.spec.js (new) — ordering, section entries and interleaving, omitted entry point, iframe entries, memoisation.
  • build-integrity.spec.js — catalog shows Library alone; handbook's two dropdowns and their pages; an app page shows only its own entries in manifest order; section dropdown contents; apps without a manifest show nothing after their name; every menu link resolves to a built page; aria-current per page type; compact sheet contents.
  • standalone.spec.js — 320–1920px: right rendering, zero horizontal overflow or clipped control; Library at the catalog's x on app pages, brand never covering it; exclusive section dropdowns (handbook); sheet contents, close/Escape; entries follow the app across navigation; keyboard operation, focus ring, outside click, tab-out, navigation.
  • web-fragment.spec.js — section dropdown and sheet inside the fragment, before and after router swaps (app → Library → app), both embeddings (plain + pierced).
  • css-isolation.spec.js — runs at 1600px so the brand is part of the compared masthead.

Local: npm test 446 passed; playwright.config.ci.js 38 passed. test:container not run (Docker).

🤖 Generated with Claude Code

@oto-macenauer-absa
oto-macenauer-absa force-pushed the feat/masthead-nav branch 2 times, most recently from 4deada2 to 2c06445 Compare September 30, 2026 09:54
The masthead's navigation is scoped to the app being viewed: Library, the
app's name, then that app's own entries from its kb-docs.json `pages` —
a page without a `section` as a link, a section as a dropdown of its
pages, placed where its first page falls in `order`. A publisher adds a
page to the menu without a knowledge base change, and the bar does not
grow with the number of apps: the catalog is the way between them. The
model is built once per resolved registry (src/utils/navigation.js) and
shares route derivation with apps.js, so every menu link resolves to a
built page.

Tablet first: below 1024px a compact bar (Library, current app, and a
Menu button for an app with manifest pages) opens a popover sheet of the
app's pages; from 1024px a wrapping bar with <details> dropdowns. Both
work without script; masthead-nav.js adds Escape, outside-click and
edge-flip to the dropdowns, taking the real window from event.view
because reframed reports its iframe's document as the masthead's
ownerDocument. The current page carries aria-current="page", the app and
section holding it aria-current="true".

The brand wordmark on app pages moves out of flow into the left gutter,
shown from 90rem where the gutter holds it: as a flex item it took its
width out of the centred column and moved every link away from where it
sits on the catalog.

The vendored docs-example fixture gains a third app, `handbook`, with a
two-section manifest, so the app-scoped menu and exclusive dropdowns are
exercised and visible in a local build.

Closes #111

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dynamic responsive masthead menu from documentation manifests

1 participant