Skip to content

fix(docs): keep SPA transitions via astro-vtbot so search survives nav - #309

Merged
EricAndrechek merged 7 commits into
mainfrom
docs-search
Jun 10, 2026
Merged

fix(docs): keep SPA transitions via astro-vtbot so search survives nav#309
EricAndrechek merged 7 commits into
mainfrom
docs-search

Conversation

@EricAndrechek

@EricAndrechek EricAndrechek commented Jun 10, 2026

Copy link
Copy Markdown
Member

Problem

On the live docs site, Starlight's built-in search breaks after the first in-page navigation — the search box disappears and the console shows:

Uncaught InvalidStateError: Failed to execute 'showModal' on 'HTMLDialogElement': The element is not in a Document.

The mobile menu breaks the same way. Root cause: the design-token rebuild (#142) added a bare <ClientRouter/> (Astro View Transitions) to Head.astro. Starlight does not support a bare <ClientRouter/> — its lead maintainer warns it breaks search and the mobile menu (withastro/starlight#2823). The SPA router swaps the whole header on every navigation, which:

  1. Leaves the previous page's window ⌘K listener pointing at a now-detached <dialog> → the next ⌘K calls showModal() on an element no longer in the document → InvalidStateError.
  2. Never re-runs Pagefind's init (gated on a one-shot DOMContentLoaded) → after the first search the box reopens empty.

Approach (revised)

An earlier revision of this PR fixed the bug by dropping the SPA router for browser-native cross-document view transitions. That worked, but it gives up the SPA niceties — instant navigation, preserved sidebar scroll, smooth transitions — and on a cold/heavy page Chromium skips the transition to a plain full reload. The preview looked noticeably worse, so this revision takes a different path.

Keep the SPA model and fix search the supported way: the astro-vtbot Starlight integration that #2823 points to. Head.astro now wraps Starlight's Head in astro-vtbot/components/starlight/Base.astro, which supplies:

  • <ClientRouter fallback="swap"/> — the same SPA transitions as before (instant nav, no full-page flash).
  • <ReplacementSwap/> — swaps only the main content frame and preserves the rest of the header, so the search <dialog> and its ⌘K listener are never detached. This is the precise fix for the showModal crash.
  • <StarlightConnector/> — re-runs Starlight's own per-navigation init: closes the mobile menu, updates the current-page marker, and keeps the existing sidebar DOM when its links are unchanged (preserving scroll position).

Net change vs main is four files: Head.astro (the wrapper), docs/package.json + pnpm-lock.yaml (the astro-vtbot devDep), and a CHANGELOG.md entry. The other docs components keep the astro:page-load / astro:after-swap re-init they already have on main — navigations are still Astro View Transitions, so nothing else changes. (Head.astro's large line count is mostly re-indentation from wrapping the existing head content; the two inline <script> blocks are byte-unchanged from maingit diff -w confirms.)

Trade-off

Adds the astro-vtbot dependency — single-maintainer, but the de-facto Astro View Transitions library and the path the Starlight project itself points to in #2823. Astro 6 compatible (the package builds against Astro 6 and declares no peer-dep constraints).

Verification

Driven with Playwright against a production build (astro build + the Cloudflare Worker), not dev:

  • ✅ SPA navigation preserves the JS context (no full reload).
  • ✅ ⌘K search opens and returns results after navigation — the reported bug — with no showModal error, and exactly one <dialog> in the document.
  • ✅ Mobile menu opens, auto-closes on navigation, and re-opens afterward (the original breakage).
  • make ci green; astro check clean.

🤖 Generated with Claude Code

Starlight's built-in search broke after the first in-page navigation:
the search box vanished and the console showed `InvalidStateError:
Failed to execute 'showModal' on 'HTMLDialogElement': The element is
not in a Document`.

Root cause: Head.astro added a bare `<ClientRouter/>` (#142), which
Starlight does not support out of the box — the lead maintainer warns
it breaks search and the mobile menu (withastro/starlight#2823), and
it's unchanged on Starlight main. The SPA router swaps the header on
every navigation, leaving the previous page's window `keydown` listener
pointing at a detached `<dialog>` (showModal throws) and never
re-initializing Pagefind (gated on a one-shot `DOMContentLoaded`), so
the modal renders empty. The mobile menu broke the same way.

Fix: drop `<ClientRouter/>` and use browser-native cross-document view
transitions (`@view-transition { navigation: auto }` in global.css).
Real navigations mean Starlight's search and mobile menu work unmodified.
- Remove the now-inert `astro:page-load` / `astro:after-swap` listeners
  across the components; each already inits on `DOMContentLoaded`, so
  behavior is unchanged under full-page navigation.
- Guard the View-Transitions-spec "Transition was skipped" rejection
  (graceful skip on slow loads) so it stays out of the console / error
  tracking.

Verified on a production build (astro build + worker) with Playwright:
search populates after navigation with no showModal error; mobile menu
toggles after navigation; native VT activates and skip-rejections stay
silent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@EricAndrechek, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 26 minutes and 44 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5a6bc493-73c7-474a-9e7e-e0b4f949ee20

📥 Commits

Reviewing files that changed from the base of the PR and between ad78256 and c93663a.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (2)
  • CHANGELOG.md
  • docs/package.json
📝 Walkthrough

Walkthrough

This PR fixes docs search and mobile menu state loss during in-page navigation by adopting the astro-vtbot integration for Starlight. The dependency is added to docs/package.json, the Head component is refactored to wrap content in VtbotStarlight instead of ClientRouter, and a changelog entry documents the fix.

Changes

Docs Search and Mobile Menu Navigation Fix

Layer / File(s) Summary
Astro-vtbot dependency and Head component wrapper integration
docs/package.json, docs/src/components/Head.astro
Add astro-vtbot at version ^2.1.12 to devDependencies. Update imports to use VtbotStarlight and Starlight's Default head component, wrapping the slot and PostHog within the <VtbotStarlight viewTransitionsFallback="swap"> router instead of the standalone ClientRouter.
Inline theme-color and favicon behavior preservation
docs/src/components/Head.astro
Preserve existing inline scripts for theme-color switching (MutationObserver and matchMedia listening) and favicon swapping (removal/append of SVG icon links) within the new VtbotStarlight wrapper. Both scripts use window sentinels (window.__whThemeColorBound and window.__whFaviconBound) and astro:after-swap handlers to prevent duplicate registration across view transitions.
Changelog entry for navigation fix
CHANGELOG.md
Document restoration of docs search and mobile menu across in-page navigation by switching to the supported astro-vtbot Starlight integration.

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main fix: using astro-vtbot to preserve SPA transitions while fixing search functionality after navigation.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, explaining the problem, approach, trade-offs, and verification of the fix.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch docs-search
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch docs-search

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added documentation Improvements or additions to documentation area/docs Documentation, site/, README labels Jun 10, 2026
@github-actions

github-actions Bot commented Jun 10, 2026

Copy link
Copy Markdown

📚 Docs preview is livehttps://548f6b8d-wavehouse-docs.wave-rf.workers.dev

  • Commitc93663a: Merge remote-tracking branch 'origin/main' into docs-search
  • Author@EricAndrechek
  • Committed — 2026-06-09 21:38 (UTC-04:00)
  • Deployed — 2026-06-09 21:44 EDT

EricAndrechek and others added 3 commits June 9, 2026 20:39
…es nav

Supersedes the cross-document-VT approach in f1079ec. The design-token
rebuild (#142) put a bare <ClientRouter/> in Head.astro, which Starlight does
not support: the SPA router swapped the whole header on every navigation, so
the prior page's window ⌘K handler pointed at a now-detached <dialog>
(InvalidStateError on showModal), and Pagefind's one-shot DOMContentLoaded
init never re-ran — search broke, and the mobile menu with it
(withastro/starlight#2823).

Rather than drop the SPA model (instant nav, preserved sidebar scroll) for
browser-native cross-document transitions, adopt astro-vtbot's Starlight
integration — the supported path #2823 names. Head.astro wraps Starlight's
Head in astro-vtbot/components/starlight/Base.astro, which supplies
<ClientRouter fallback="swap"/> plus:
  - <ReplacementSwap/> — swaps only the main content frame and preserves the
    rest of the header, so the search <dialog> and its ⌘K listener are never
    detached.
  - <StarlightConnector/> — re-runs Starlight's per-nav init: closes the
    mobile menu, updates the current-page marker, and keeps the existing
    sidebar DOM when its links match (preserving scroll position).

The docs components keep the astro:page-load / astro:after-swap re-init they
have on main (navigations are still Astro View Transitions), so nothing else
changes.

Verified on a production build (astro build + Cloudflare Worker) with
Playwright: SPA nav preserves the JS context (no full reload); ⌘K search opens
and returns results after nav with no showModal error and one <dialog>; the
mobile menu opens, auto-closes on nav, then re-opens.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The CHANGELOG entry carries the full rationale; keep the inline comment concise
(pre-push review [MAY] + AGENTS.md craftsmanship). Comment-only — the two inline
scripts are unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@EricAndrechek EricAndrechek changed the title fix(docs): native view transitions so search survives navigation fix(docs): keep SPA transitions via astro-vtbot so search survives nav Jun 10, 2026
@github-actions github-actions Bot added the dependencies Pull requests that update a dependency file label Jun 10, 2026
@EricAndrechek

Copy link
Copy Markdown
Member Author

@coderabbitai review

Heads up: this PR was reframed since your last (rate-limited) pass — it now swaps the bare <ClientRouter/> for the supported astro-vtbot Starlight integration (restoring SPA view transitions while keeping search + mobile menu working). Net delta vs main is 4 files: docs/src/components/Head.astro, docs/package.json, pnpm-lock.yaml, CHANGELOG.md.

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -e

# Get PR details and diff
gh pr view 309 --repo Wave-RF/WaveHouse

echo "=== PR FILES CHANGED ==="
gh pr diff 309 --repo Wave-RF/WaveHouse

Length of output: 24546


🏁 Script executed:

#!/bin/bash
set -e

# Read key files
echo "=== docs/src/components/Head.astro ==="
cat docs/src/components/Head.astro

echo ""
echo "=== docs/package.json ==="
cat docs/package.json

echo ""
echo "=== CHANGELOG.md ==="
cat CHANGELOG.md

Length of output: 208884



@EricAndrechek Thanks for the context on the reframe — the diff reads cleanly. Here's my review of the four changed files.


Review of PR #309

docs/src/components/Head.astro

The core change is correct. Wrapping the entire head content in <VtbotStarlight viewTransitionsFallback="swap"> instead of a bare <ClientRouter fallback="swap" /> is the supported path for Starlight (ref: withastro/starlight#2823), and viewTransitionsFallback="swap" faithfully preserves the original fallback behaviour.

A few specific checks:

  • <PostHog /> inside the wrapper — fine. PostHog already initialises with capture_pageview: 'history_change' so it handles soft navigations correctly regardless of wrapper placement.
  • Both is:inline script blocks — byte-for-byte unchanged from main. The astro:after-swap listeners and window.__whThemeColorBound / window.__whFaviconBound sentinel guards are still appropriate: astro:after-swap continues to fire on every vtbot-managed SPA navigation, and ReplacementSwap preserves the <head> DOM so the listeners accumulate correctly under the guard.
  • <Default><slot /></Default> placement — correct. vtbot's Base.astro needs to see the full head content to manage it across transitions.

No issues here.


docs/package.json — minor observation

astro-vtbot lands in devDependencies, while astro and @astrojs/starlight live in dependencies. For a "private": true SSG site deployed as a static bundle the distinction is purely cosmetic (npm/pnpm won't omit devDeps in a build), but if you'd like consistency, moving it to dependencies alongside the other Astro packages would make the intent clearer. Totally fine to leave as-is.


pnpm-lock.yaml

Snapshot adds astro-vtbot@2.1.12 plus five @vtbag/* sub-packages. The package declares no peer-dep constraints per the PR body, and the project is already on Astro 6.3.7, so compatibility is confirmed. Lockfile looks clean.


CHANGELOG.md

Entry is correctly placed under ### Fixed## Unreleased, cross-references withastro/starlight#2823, and the files listed (docs/package.json, pnpm-lock.yaml, docs/src/components/Head.astro) match the actual diff.


Summary

The change is minimal, targeted, and uses the officially recommended integration. The only thing worth considering before merging:

  1. devDependencies vs dependencies for astro-vtbot — harmless inconsistency, trivial to normalise if desired.
  2. Single-maintainer dependency — already acknowledged as a conscious trade-off in the PR description. No action needed unless the project has a stricter policy here.

Everything else looks good. Ready to move out of draft when you're satisfied with the verification pass.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 77ee70f2-592c-4a3f-97ce-cdfb756a61d6

📥 Commits

Reviewing files that changed from the base of the PR and between ab02b02 and ad78256.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (3)
  • CHANGELOG.md
  • docs/package.json
  • docs/src/components/Head.astro
📜 Review details
⏰ Context from checks skipped due to timeout of 300000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: CI
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js,jsx,json,md,mdx}

📄 CodeRabbit inference engine (AGENTS.md)

Biome owns JavaScript/TypeScript/JSON linting and formatting; markdownlint owns Markdown style; misspell owns spelling — all under make lint / make fix; accuracy/clarity/doc-sync is reviewed separately by docs-reviewer

Files:

  • docs/package.json
  • CHANGELOG.md
{clients/ts/**/*,tests/e2e/sdk/**/*,docs/**/*}

📄 CodeRabbit inference engine (AGENTS.md)

Use pnpm (≥ 11.1) + Node 22 LTS (.nvmrc) for JavaScript/TypeScript; make tools runs one root pnpm install --frozen-lockfile across three workspaces (SDK clients/ts/, E2E tests/e2e/sdk/, docs docs/)

Files:

  • docs/package.json
  • docs/src/components/Head.astro
🔇 Additional comments (5)
docs/src/components/Head.astro (4)

1-17: LGTM!

Also applies to: 119-119


28-70: LGTM!


84-118: LGTM!


18-27: LGTM!

CHANGELOG.md (1)

23-23: LGTM!

Comment thread docs/package.json
@github-project-automation github-project-automation Bot moved this from Backlog to In review in WaveHouse Task Board Jun 10, 2026
EricAndrechek and others added 3 commits June 9, 2026 21:22
astro-vtbot is a build-time Astro/Starlight integration like @astrojs/starlight
and the starlight-* plugins (all in `dependencies`); `devDependencies` here is
genuine tooling (playwright, wrangler, workers-types). Aligns the grouping.

Addresses the CodeRabbit review on #309.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@EricAndrechek
EricAndrechek marked this pull request as ready for review June 10, 2026 01:39
@EricAndrechek
EricAndrechek requested review from a team and taitelee June 10, 2026 01:39
@EricAndrechek
EricAndrechek merged commit 40619b8 into main Jun 10, 2026
9 of 11 checks passed
@EricAndrechek
EricAndrechek deleted the docs-search branch June 10, 2026 01:50
@github-project-automation github-project-automation Bot moved this from In review to Done in WaveHouse Task Board Jun 10, 2026
EricAndrechek added a commit that referenced this pull request Jun 10, 2026
…#313)

## What

A broad polish pass over the docs site, building on #309's astro-vtbot
SPA integration — evolved across several overnight cycles from
PR-preview feedback. (The direction-aware page slides + title morph from
round 1 were tried and **ripped out** on review — too much motion for
docs.)

### Progress feedback
- **Navigation progress bar** — thin accent bar on slow loads (grace
delay keeps fast navs clean). Self-hosted on vtbot's `loading()` hook:
vtbot's own `<ProgressBar/>` imports Swup's plugin **from unpkg.com at
runtime in every visitor's browser** (verified in the built bundle),
which fails our supply-chain bar.
- **Reading-progress bar** (`ReadingProgress.astro`) — a 2px accent line
under the header filling with how far through the article you've
scrolled; spans only the content column, hidden ≥72rem where the
right-rail TOC takes over. The mobile answer to "where am I on this
page".
- Hover **prefetch** verified already active via ClientRouter (no change
needed); reduced-motion guard zeroes all view-transition animations.

### Starlight chrome
- **Search dialog branded** (three rounds) — design-system frame + pop,
blurred backdrop, code-surface input, card result groups with header
band + surface-tint row hover, thin branded scrollbars (dialog frame
scroller included), accent `<mark>` hits, branded clear/load-more
controls.
- **Asides on brand hues** — and then *used*: nineteen titled
note-blockquotes across api/pipes/access-control/sdk/development became
real `:::note`/`:::caution` asides (sharp edges → caution,
clarifications → note).
- **Tabs as a segmented control**, prose `kbd`/`mark`, accent anchor
hover, inner-surface scrollbars, mobile menu button kept on the surface
scale, h2 section rule fades out instead of boxing each section.

### Landing page
- New **SDK showcase** — "Query it like a database. Subscribe to it like
a socket." with Ingest / Query / Live-updates tabs, every snippet
type-checked against `clients/ts/src` by reviewers. This surfaced a
**latent bug in sdk.md's Live Queries example** (chained `.where()` off
`from()`, which has no builder methods) — fixed in both places.
- The orphan "How it ships" card folded into the quickstart prose.

### Docs IA + content
- Sidebar gains a **Reference** group (API Reference, TypeScript SDK) —
lookup vs narrative; no URL changes.
- **Getting Started → "Troubleshooting first runs"**: the five
first-session trip-ups (schema-refresh 404, pre-flush empty query,
fail-closed 403 on non-demo tables, port conflicts, cold-start timing),
every claim verified against server code + compose by both reviewers.

### 404 — "signal lost"
Landing atmosphere, mono 404, an **EKG trace** (heartbeat decaying to
flatline, redrawn by a looping monitor-sweep over a live→dead gradient,
dim static ghost underneath, blinking sample dot; static for reduced
motion), card quick links, exactly one accessible heading.

### PostHog
- SPA pageview setup verified correct as-is (`history_change` + init
sentinel); added `capture_exceptions: true` for error tracking on docs
JS.

## Verification
- Playwright against the production build each cycle: search survives
SPA navs, reading bar 0→0.53→1.0 through a long article @390px and
re-arms after navigation, progress bar under throttled fetch, 404
single-heading, hover-prefetch live, no console errors.
- Screenshot corpus across 390/768/1440/1920/2560px × dark/light incl.
search/mobile-nav open; wide-viewport per-element overflow check
(1920+2560) clean.
- Lighthouse (local, prod build): **95 perf / 100 a11y / 100
best-practices / 92 SEO**, CLS 0.039, LCP intact.
- `make ci` green + both pre-push reviewers `ship_it` on every push (9
review rounds; 2 iterate rounds caught real bugs — the sdk.md live-query
defect and two aside-conversion artifacts — both fixed).

Deliberately *not* done: splitting api.md/sdk.md (URL churn + redirects
felt wrong to ship overnight without feedback — happy to do as a
follow-up), Steps adoption on getting-started (would drop the numbered
sections from the TOC).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/docs Documentation, site/, README dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

1 participant