From 87d5232340c7a27a8b1eb329b900e3c9bebebcbb Mon Sep 17 00:00:00 2001 From: ubay1 Date: Thu, 12 Nov 2026 20:57:40 +0700 Subject: [PATCH 1/3] fix(sidebar): scroll active item into view on page load --- components/Sidebar/index.jsx | 44 ++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/components/Sidebar/index.jsx b/components/Sidebar/index.jsx index 3eb0c6a..d7e87b6 100644 --- a/components/Sidebar/index.jsx +++ b/components/Sidebar/index.jsx @@ -1,3 +1,4 @@ +import { useLayoutEffect } from 'react'; import SideBar from '@node-core/ui-components/Containers/Sidebar'; import withIsland from '@doc-kit/generator-react/html/ui/islands/withIsland.jsx'; import { sidebar } from '../../site.json' with { type: 'json' }; @@ -16,14 +17,39 @@ const PrefetchLink = props => ; * * @param {{ pathname: string }} props */ -const Sidebar = ({ pathname }) => ( - -); +const Sidebar = ({ pathname }) => { + // Each navigation is a full page load, so the sidebar always starts at the + // top. Scroll the active link into view (stateless, no storage needed). + useLayoutEffect(() => { + const active = document.querySelector(`aside a[href="${pathname}"]`); + const aside = active?.closest('aside'); + + if (!aside || !active) { + return; + } + + const asideRect = aside.getBoundingClientRect(); + const activeRect = active.getBoundingClientRect(); + + // Only scroll if the active link is outside the visible area + if ( + activeRect.top < asideRect.top || + activeRect.bottom > asideRect.bottom + ) { + aside.scrollTop += + activeRect.top - asideRect.top - aside.clientHeight / 2; + } + }, [pathname]); + + return ( + + ); +}; export default withIsland(Sidebar, { name: 'Sidebar', on: { idle: true } }); From b738bd224f22b884c1971450ce7168dfa37b0a71 Mon Sep 17 00:00:00 2001 From: ubay1 Date: Thu, 12 Nov 2026 21:31:31 +0700 Subject: [PATCH 2/3] fix(sidebar): restore sidebar scroll container styling on is-land wrapper doc-kit targets `is-land[data-island-name='SideBar']` while this repo registers the island as `Sidebar`, leaving it with `display: contents` and breaking sticky/overflow scrolling on the sidebar. --- template.html | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/template.html b/template.html index c7ecc11..556c25e 100644 --- a/template.html +++ b/template.html @@ -10,6 +10,20 @@ rel="canonical" href="https://nodejs.org/learn${metadata.path.replace('/index', '')}" /> + ${preloads} ${assets} ${head} From da5971acef1a1870ce28a29089b412edf853dba8 Mon Sep 17 00:00:00 2001 From: ubay1 Date: Thu, 12 Nov 2026 21:34:56 +0700 Subject: [PATCH 3/3] fix logic scroller --- components/Sidebar/index.jsx | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/components/Sidebar/index.jsx b/components/Sidebar/index.jsx index d7e87b6..c53fa96 100644 --- a/components/Sidebar/index.jsx +++ b/components/Sidebar/index.jsx @@ -23,21 +23,31 @@ const Sidebar = ({ pathname }) => { useLayoutEffect(() => { const active = document.querySelector(`aside a[href="${pathname}"]`); const aside = active?.closest('aside'); + const scroller = + aside && aside.scrollHeight > aside.clientHeight + ? aside + : aside?.parentElement; - if (!aside || !active) { + if ( + !scroller || + !active || + scroller.scrollHeight <= scroller.clientHeight + ) { return; } - const asideRect = aside.getBoundingClientRect(); + console.log(pathname); + + const scrollerRect = scroller.getBoundingClientRect(); const activeRect = active.getBoundingClientRect(); // Only scroll if the active link is outside the visible area if ( - activeRect.top < asideRect.top || - activeRect.bottom > asideRect.bottom + activeRect.top < scrollerRect.top || + activeRect.bottom > scrollerRect.bottom ) { - aside.scrollTop += - activeRect.top - asideRect.top - aside.clientHeight / 2; + scroller.scrollTop += + activeRect.top - scrollerRect.top - scroller.clientHeight / 2; } }, [pathname]);