diff --git a/.changeset/three-queens-pretend.md b/.changeset/three-queens-pretend.md
new file mode 100644
index 000000000..138fb4bc8
--- /dev/null
+++ b/.changeset/three-queens-pretend.md
@@ -0,0 +1,5 @@
+---
+"gitbook": patch
+---
+
+Page outline: scroll to active item
diff --git a/packages/gitbook/src/components/PageAside/PageAside.tsx b/packages/gitbook/src/components/PageAside/PageAside.tsx
index 4f3604fc8..c9f74ff41 100644
--- a/packages/gitbook/src/components/PageAside/PageAside.tsx
+++ b/packages/gitbook/src/components/PageAside/PageAside.tsx
@@ -40,6 +40,7 @@ export function PageAside(props: {
'group/aside',
'hidden',
'pt-8',
+ 'pb-4',
'xl:flex',
'xl:max-3xl:chat-open:hidden',
@@ -111,7 +112,7 @@ export function PageAside(props: {
>
{page.layout.outline ? (
-
+
{document ? (
@@ -165,11 +166,7 @@ async function PageAsideSections(props: { document: JSONDocument; context: GitBo
const sections = await getDocumentSections(context, document);
- return sections.length > 1 ? (
-
-
-
- ) : null;
+ return sections.length > 1 ? : null;
}
function PageAsideActions(props: {
@@ -199,7 +196,7 @@ function PageAsideActions(props: {
'border-t',
'first:border-none',
'border-tint-subtle',
- 'py-5',
+ 'pt-5',
'first:pt-0',
'xl:max-2xl:page-api-block:p-5',
'empty:hidden'
@@ -275,7 +272,7 @@ async function PageAsideFooter(props: { context: GitBookSiteContext }) {
'sticky bottom-0 z-10 mt-auto flex flex-col',
'bg-tint-base theme-gradient-tint:bg-gradient-tint theme-gradient:bg-gradient-primary theme-muted:bg-tint-subtle [html.sidebar-filled.theme-bold.tint_&]:bg-tint-subtle',
'border-tint-subtle xl:max-2xl:page-api-block:border-t xl:max-2xl:page-api-block:p-2',
- 'py-4'
+ 'pt-4'
)}
>
{/* Mode Switcher */}
diff --git a/packages/gitbook/src/components/PageAside/ScrollSectionsList.tsx b/packages/gitbook/src/components/PageAside/ScrollSectionsList.tsx
index 4d3dc0e6e..0992b722f 100644
--- a/packages/gitbook/src/components/PageAside/ScrollSectionsList.tsx
+++ b/packages/gitbook/src/components/PageAside/ScrollSectionsList.tsx
@@ -1,5 +1,4 @@
'use client';
-import { motion } from 'framer-motion';
import React from 'react';
import { useScrollActiveId } from '@/components/hooks';
@@ -15,6 +14,11 @@ import { AsideSectionHighlight } from './AsideSectionHighlight';
*/
const SECTION_INTERSECTING_THRESHOLD = 0.9;
+/**
+ * The offset from the top of the page when scrolling to the active item.
+ */
+const ACTIVE_ITEM_OFFSET = 100;
+
const springCurve = {
type: 'spring',
stiffness: 700,
@@ -39,10 +43,25 @@ export function ScrollSectionsList(props: { sections: DocumentSection[] }) {
enabled,
});
+ const scrollContainerRef = React.useRef(null);
+ const activeItemRef = React.useRef(null);
+
+ React.useEffect(() => {
+ if (activeId && activeItemRef.current && scrollContainerRef.current) {
+ scrollContainerRef.current?.scrollTo({
+ top: activeItemRef.current.offsetTop - ACTIVE_ITEM_OFFSET,
+ behavior: 'smooth',
+ });
+ }
+ }, [activeId]);
+
return (
-
+
{sections.map((section) => (
- 1 && ['ml-3', 'my-0', 'sidebar-list-line:ml-0']
)}
+ ref={activeId === section.id ? activeItemRef : null}
>
{activeId === section.id ? (
-
+
))}
);