docs(ui): scroll the sidebar so the active (bold) link is in view on load

Deep-linking into a low doc (e.g. ratings/get_ratings from the overview
page) opened the right menus but sat the highlight below the sidebar's
visible scroll area. scrollSidebarToActive() runs at docs Alpine init:
finds the link for the current path, and if it's outside the sidebar's
scrollport, moves ONLY the sidebar's own scrollTop to clamp it into the
nearest edge — never the page scroll. Skips the off-canvas mobile
drawer. (Same behavior as Docusaurus/MkDocs on deep links.)

Live: x-init wired on docs pages, function in the shipped bundle,
make test green, 146 vitests.
This commit is contained in:
John O'Keefe committed 2026-10-24 21:57:03 -04:00
1 parent a1ac5b1541
commit da0bae94bb
3 files changed
+30 -2

No files matched your search

+28
View File
@@ -37,6 +37,33 @@ function toggleCollapsible(header: HTMLElement): void {
applyCollapsed(items, !isCollapsed);
}
// scrollSidebarToActive brings the current page's (bold) nav link into the
// sidebar's visible area on load — deep links from search, bookmarks, or
// the overview page otherwise open with the highlight scrolled out of
// sight. Only the sidebar's own scroll position changes, never the page.
function scrollSidebarToActive(): void {
const sidebar = document.getElementById("docs-sidebar");
if (!sidebar) return;
// Skip the off-canvas mobile drawer (translated off screen).
if (sidebar.classList.contains("-translate-x-full")) return;
const active = sidebar.querySelector<HTMLAnchorElement>(
`a[href="${location.pathname}"], a[href="${location.pathname.replace(/\.md$/, "")}"]`,
);
if (!active) return;
const sb = sidebar.getBoundingClientRect();
const ar = active.getBoundingClientRect();
if (ar.top >= sb.top && ar.bottom <= sb.bottom) return; // already visible
if (ar.top < sb.top) {
sidebar.scrollTop += ar.top - sb.top;
} else {
sidebar.scrollTop += ar.bottom - sb.bottom;
}
}
let docsSearchTimeout: ReturnType<typeof setTimeout> | null = null;
let docsSearchAbort: AbortController | null = null;
@@ -126,4 +153,5 @@ Alpine.data("docs", () => ({
toggleCollapsible,
initializeSearch: initializeDocsSearch,
initializeCodeCopyButtons,
scrollSidebarToActive,
}));