fix: static SiteHeader (no hydration) + build-time guards against hydration regressions - #57
Merged
Merged
Conversation
…ainst hydration regressions The header island broke every page: <Teleport to="body"> renders SSR teleport anchors that the browser cannot match on hydration (mismatch errors, dead header), and build.format 'file' leaked '/leadership.html' into the serialized current-path prop and the canonical URLs. - SiteHeader is now an Astro component with zero hydration: CSS hover dropdowns, vanilla-script mobile drawer and theme toggle, scroll state via one class toggle. Vue islands remain only on the home hero - BaseLayout strips the .html and /index suffixes for canonicals and nav state (home canonical was '/index') - scripts/islands.test.ts: architectural guards — no Teleport, no vue-router, no browser globals in island render paths - scripts/verify-dist.ts (wired into npm run build): post-build invariants over dist/ — canonical equals the public URL on every page, no .html in serialized island props, island assets emitted and SSR content non-empty
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.
Summary
Fixes the broken pages after the Astro migration, and adds the requested tests to prevent this class of regression.
Root causes (two, compounding)
<Teleport to="body">in the SiteHeader Vue island. Vue SSR renders teleport content as anchor comments inside the island; on hydration the browser cannot match them, producing the hydration mismatches and a dead/broken header on every page.build.format: 'file'leaks file paths into URLs.Astro.url.pathnameis/leadership.htmlat build, which was serialized into the island'scurrent-pathprop and — worse — emitted as<link rel=canonical>on every page (/indexfor home).Fix
fixedpositioning holds), theme toggle whose icons swap purely viadark:classes, scroll state via one class toggle. Vue islands now exist only on the home hero, which has no Teleport and deterministic render..html, collapses/indexto/) for canonicals and nav state.Tests (the requested guardrails)
scripts/islands.test.ts(vitest, runs in CI vianpm test): islands must not use<Teleport>; must not importvue-router; must not touch browser globals (window/document/localStorage/…) outside lifecycle hooks; layout must strip the.htmlsuffix.scripts/verify-dist.ts(wired intonpm run build, fails the build): every page's canonical equals its public URL; no.htmlin serialized island props; every island'scomponent-urlasset is emitted with non-empty SSR content. This verifier is what caught the/indexhome canonical and validated the fix — build now reports67 pages, 65 canonicals, 2 islands — all invariants hold.