diff --git a/website/src/views/modules/ModulePageContent.tsx b/website/src/views/modules/ModulePageContent.tsx index 9e5964ceff..3fbc8bc63b 100644 --- a/website/src/views/modules/ModulePageContent.tsx +++ b/website/src/views/modules/ModulePageContent.tsx @@ -1,4 +1,4 @@ -import { memo, useState } from 'react'; +import { memo, useEffect, useState } from 'react'; import classnames from 'classnames'; import ScrollSpy from 'react-scrollspy'; import { kebabCase, map, mapValues, values, sortBy } from 'lodash-es'; @@ -69,6 +69,21 @@ const ModulePageContent: React.FC = ({ module, archiveYear }) => { useScrollToTop(); + // Smoothly scroll to section anchors from the side menu, but only while on + // this page. `scroll-behavior` must live on the scrolling root (`html`), so + // we scope it to the module page's lifecycle here instead of a global + // stylesheet rule that would leak to every other route. Respects + // `prefers-reduced-motion` for users who opt out of animations. + useEffect(() => { + if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return undefined; + + const { style } = document.documentElement; + style.scrollBehavior = 'smooth'; + return () => { + style.scrollBehavior = ''; + }; + }, []); + return (
{pageTitle}