diff --git a/components/Sidebar/index.jsx b/components/Sidebar/index.jsx
index 3eb0c6a..c53fa96 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,49 @@ 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');
+ const scroller =
+ aside && aside.scrollHeight > aside.clientHeight
+ ? aside
+ : aside?.parentElement;
+
+ if (
+ !scroller ||
+ !active ||
+ scroller.scrollHeight <= scroller.clientHeight
+ ) {
+ return;
+ }
+
+ 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 < scrollerRect.top ||
+ activeRect.bottom > scrollerRect.bottom
+ ) {
+ scroller.scrollTop +=
+ activeRect.top - scrollerRect.top - scroller.clientHeight / 2;
+ }
+ }, [pathname]);
+
+ return (
+
+ );
+};
export default withIsland(Sidebar, { name: 'Sidebar', on: { idle: true } });
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}