Keep the docs sidebar mounted across navigation

Draft
MA
3d ago
+215-199

Problem

Every docs page built its own copy of the desktop sidebar inside its route component. Each client-side navigation therefore unmounted it and mounted a fresh one, which:

  • reset the sidebar's scroll position (most noticeable when switching between package changelogs under API Reference)
  • collapsed any groups the reader had expanded
  • re-rendered the whole sidebar tree on every page change

Change

  • One persistent sidebar. The desktop sidebar is registered once as an app wrap (extra_app_wraps), outside the page routes, so navigation no longer unmounts it.
  • Reads the URL in the browser. The sidebar gets the current path from react-router's useLocation instead of from values computed per page in Python (calculate_index / normalize_url are removed). A group is open when the current route is one of its descendant pages.
  • Spacer slot. Each docpage reserves the column with an empty .docs-sidebar-slot. The shared sidebar is fixed-positioned over it, and a CSS :has rule hides it on pages without a slot (404, redirects).
  • Scrolls only when needed. If the active link is off-screen after navigating (prev/next, in-content links), the sidebar scrolls just enough to show it. This replaces the old on_mount script, which added a new popstate listener on every mount.
  • Truncated long labels (reflex-site-shared): a selected leaf such as reflex-components-react-player used to wrap past its fixed-height highlight. Leaf labels now truncate with an ellipsis, and the full name shows on hover.

Behavior notes

  • A group now opens whenever it contains the current page. The old index logic opened only the first match.
  • Groups a reader expands by hand stay open across navigation.
  • The mobile drawer still builds its own sidebar per page, which is fine since it closes on navigation.

Testing

  • New unit test for URL-driven group expansion; existing sidebar unit tests updated for the new signatures.
  • New browser tests: the sidebar keeps the same element and scroll offset across navigation, and a long active label stays on one line.
  • All 11 existing sidebar browser tests pass against a local dev server (built from a whitelisted subset of pages).
  • test_sidebar_offsets_follow_navbar_and_announcement failed once on a freshly compiled dev server and passed on every re-run. It looks like a pre-existing hydration race on the "Dismiss announcement" click, but I haven't checked it against main.

šŸ¤– Generated with Claude Code