feat(site): preserve scroll position on framework switch (pagereveal) (#608)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Darius Cepulis
2026-02-25 14:29:54 -06:00
committed by GitHub
co-authored by Claude Opus 4.6
parent 1c6293ea39
commit 420742e7c9
3 changed files with 63 additions and 28 deletions
@@ -15,39 +15,55 @@ const { docsSidebarId } = Astro.props;
const STORAGE_KEY = 'vjs-sidebar-state';
window.addEventListener('pagereveal', () => {
// Restore sidebar state
const aside = document.getElementById(docsSidebarId);
if (!aside) return;
const stored = sessionStorage.getItem(STORAGE_KEY);
if (aside) {
const stored = sessionStorage.getItem(STORAGE_KEY);
if (stored) {
try {
const state = JSON.parse(stored);
if (stored) {
try {
const state = JSON.parse(stored);
if (state.sidebarScroll !== undefined) {
aside.scrollTop = state.sidebarScroll;
if (state.sidebarScroll !== undefined) {
aside.scrollTop = state.sidebarScroll;
}
if (state.detailsState) {
document.querySelectorAll(`#${docsSidebarId} details[id]`).forEach((details) => {
const savedState = state.detailsState[details.id];
if (savedState !== undefined) {
details.open = savedState;
}
});
}
} catch (e) {
console.error('[Sidebar] Failed to restore state', e);
}
}
if (state.detailsState) {
document.querySelectorAll(`#${docsSidebarId} details[id]`).forEach((details) => {
const savedState = state.detailsState[details.id];
if (savedState !== undefined) {
details.open = savedState;
}
});
// Auto-expand sections containing active link (override saved state)
const activeLink = aside.querySelector('a[aria-current="page"]');
if (activeLink) {
let parent = activeLink.closest('details');
while (parent && aside.contains(parent)) {
parent.open = true;
parent = parent.parentElement?.closest('details') ?? null;
}
} catch (e) {
console.error('[Sidebar] Failed to restore state', e);
}
}
// Auto-expand sections containing active link (override saved state)
const activeLink = aside.querySelector('a[aria-current="page"]');
if (activeLink) {
let parent = activeLink.closest('details');
while (parent && aside.contains(parent)) {
parent.open = true;
parent = parent.parentElement?.closest('details') ?? null;
// Restore page scroll position (set by framework switch)
try {
const scrollData = sessionStorage.getItem('vjs-page-scroll');
if (scrollData) {
sessionStorage.removeItem('vjs-page-scroll');
const { url, scrollY } = JSON.parse(scrollData);
if (url.replace(/\/$/, '') === window.location.pathname.replace(/\/$/, '')) {
window.scrollTo(0, scrollY);
}
}
} catch (e) {
// Ignore storage errors
}
});
+12 -2
View File
@@ -35,10 +35,20 @@ export function Selectors({ currentFramework, currentSlug }: SelectorProps) {
});
if (shouldReplace) {
// Maintaining the current slug, navigate without pushing onto the history stack
// Base UI's scroll lock transfers html.scrollTop → body.scrollTop
const scrollLocked = document.documentElement.hasAttribute('data-base-ui-scroll-locked');
const scrollY = scrollLocked ? document.body.scrollTop : window.scrollY;
try {
sessionStorage.setItem(
'vjs-page-scroll',
JSON.stringify({ url: new URL(url, window.location.origin).pathname, scrollY })
);
} catch {
// Ignore storage errors
}
window.location.replace(url);
} else {
// Changing slug, use normal navigation
window.location.href = url;
}
};
@@ -17,7 +17,6 @@ interface Props {
}
export default function JSPickerClient({ currentFramework, currentStyle, currentSlug }: Props) {
// TODO: use astro view transitions to preserve scroll position when switching from the same slug to the same slug
const handleFrameworkChange = (newFramework: SupportedFramework | null) => {
if (newFramework === null) return;
if (!isValidFramework(newFramework)) return;
@@ -30,10 +29,20 @@ export default function JSPickerClient({ currentFramework, currentStyle, current
});
if (shouldReplace) {
// Maintaining the current slug, navigate without pushing onto the history stack
// Base UI's scroll lock transfers html.scrollTop → body.scrollTop
const scrollLocked = document.documentElement.hasAttribute('data-base-ui-scroll-locked');
const scrollY = scrollLocked ? document.body.scrollTop : window.scrollY;
try {
sessionStorage.setItem(
'vjs-page-scroll',
JSON.stringify({ url: new URL(url, window.location.origin).pathname, scrollY })
);
} catch {
// Ignore storage errors
}
window.location.replace(url);
} else {
// Changing slug, use normal navigation
window.location.href = url;
}
};