feat: persistent Knowledge base masthead on every page - #25
Merged
Conversation
Closes #24. The branded header only existed on the landing catalog. Opening a documentation app dropped it, leaving no in-page way back to the catalog and no indication of which app was being viewed. Render every page through the layout ------------------------------------ Packaged sub-app pages previously bypassed Base.astro entirely: the catchall route emitted the pre-built document verbatim, so chrome, fonts, marketplace CSS, theme handling and the SPA router all had to be string-injected by transformSubAppHtml(). That forced a parallel implementation of everything the layout already did. transformSubAppHtml() now rewrites URLs and splits the document into its parts (head contents, body attributes, body contents, title), and Base.astro re-hosts them. The layout becomes the single owner of the document shell for landing, packaged and iframe pages alike. Superseded and removed: chromeHtml() and marketplaceRouterScript() — Astro's <ClientRouter /> already handled navigation on these pages, so the bespoke fetch-and-swap router was redundant. Add the masthead ---------------- Masthead.astro renders the DOCS eyebrow, title and strapline plus a sub-navigation row: "Library" linking to the catalog, and a dynamic second entry naming the app currently being viewed. The entry matching the current location is inert text with aria-current="page" rather than a link to itself. Drop the chrome bar and dark mode --------------------------------- With the masthead carrying the navigation, the 56px fixed chrome bar was redundant, so it is gone along with its app switcher and content offset. A fixed bar was also the piece that escaped the shadow boundary when reframed pierced the DOM. The marketplace is now light-only: no theme toggle, no persisted preference, no dark palette. transformSubAppHtml() strips a sub-app's theme bootstrap and `dark` body class unconditionally, so an embedding host's theme cannot bleed into the fragment. Notes ----- - Fixes a latent bug: Chrome.astro's theme button called toggleTheme(), which only the string-injected script defined — it was dead on every Astro-rendered page. - Iframe entries flex-fill the viewport instead of hard-coding 100vh, which would have overflowed below the masthead. - The #297 CSS-accumulation test was counting mid-swap: ClientRouter updates the URL on popstate before swapping the DOM, so neither waitForURL nor networkidle proved the swap had finished. It now waits for landing-only content. The guard itself is unchanged. - The app contract required a dark variable set and a 56px sidebar offset; both are updated. HEADLESS_RULES.md also pointed at a non-existent src/input.css, now src/styles/marketplace.css. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015MkyFmv3JPCkTaFhPVUFtg
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #24.
Problem
The branded header —
DOCSeyebrow, Knowledge base title, strapline — onlyexisted on the landing catalog. Opening a documentation app dropped it, leaving no
in-page way back to the catalog and no indication of which app was being viewed.
Render every page through the layout
Packaged sub-app pages previously bypassed
Base.astroentirely: the catchall routeemitted the pre-built document verbatim, so chrome, fonts, marketplace CSS, theme
handling and the SPA router all had to be string-injected by
transformSubAppHtml().That forced a parallel implementation of everything the layout already did — which is
why
Chrome.astroandchromeHtml()both existed and had to be kept in sync.transformSubAppHtml()now rewrites URLs and splits the document into its parts(head contents, body attributes, body contents, title);
Base.astrore-hosts them.The layout becomes the single owner of the document shell for landing, packaged and
iframe pages alike.
Superseded and removed:
chromeHtml()andmarketplaceRouterScript()— Astro's<ClientRouter />already handled navigation on these pages, so the bespokefetch-and-swap router was redundant.
Add the masthead
Masthead.astrorenders the eyebrow, title and strapline plus a sub-navigation row:The entry matching the current location is inert text with
aria-current="page"ratherthan a link to itself.
Drop the chrome bar and dark mode
With the masthead carrying the navigation, the 56px fixed chrome bar was redundant —
gone, along with its app switcher and content offset. A fixed bar was also the piece
that escaped the shadow boundary when reframed pierced the DOM.
The marketplace is now light-only: no theme toggle, no persisted preference, no dark
palette.
transformSubAppHtml()strips a sub-app's theme bootstrap anddarkbodyclass unconditionally, so an embedding host's theme cannot bleed into the fragment.
Notes for the reviewer
Chrome.astro's theme button calledtoggleTheme(), whichonly the string-injected script defined — it was dead on every Astro-rendered page.
100vh, which wouldhave overflowed below the masthead.
ClientRouterupdates the URL on popstate before swapping the DOM, so neither
waitForURLnornetworkidleproved the swap had finished. It now waits for landing-only content.The guard itself is unchanged — verified the settled count does not grow.
updated.
HEADLESS_RULES.mdalso pointed at a non-existentsrc/input.css, nowsrc/styles/marketplace.css.feat: iframe-based app onboardingcommit, which the masthead work builds on.
Testing
Both Playwright layers pass: 32 embedded (
npm test) and 15 standalone(
playwright.config.ci.js). New coverage: masthead presence and crumb behaviour acrosscatalog / app-index / deep / iframe pages, absolute-path contract for masthead links,
single-document-shell integrity after the layout re-host, and a light-only assertion
(
window.toggleThemeundefined, nomp-themein localStorage, no.darknode).🤖 Generated with Claude Code
https://claude.ai/code/session_015MkyFmv3JPCkTaFhPVUFtg