diff --git a/packages/docusaurus-theme-classic/src/theme/Navbar/index.js b/packages/docusaurus-theme-classic/src/theme/Navbar/index.js
index 0a39da9f74f5..e317c97fc756 100644
--- a/packages/docusaurus-theme-classic/src/theme/Navbar/index.js
+++ b/packages/docusaurus-theme-classic/src/theme/Navbar/index.js
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
-import React, {useCallback, useState, useEffect} from 'react';
+import React, {useCallback, useState} from 'react';
import Link from '@docusaurus/Link';
import Head from '@docusaurus/Head';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
@@ -17,6 +17,7 @@ import Toggle from '@theme/Toggle';
import classnames from 'classnames';
import useTheme from '@theme/hooks/useTheme';
+import useHideableNavbar from '@theme/hooks/useHideableNavbar';
import styles from './styles.module.css';
@@ -50,14 +51,8 @@ function Navbar() {
const [sidebarShown, setSidebarShown] = useState(false);
const [isSearchBarExpanded, setIsSearchBarExpanded] = useState(false);
const [theme, setTheme] = useTheme();
- const [isNavbarVisible, setIsNavbarVisible] = useState(true);
- const [lastScrollTop, setLastScrollTop] = useState(0);
- const [navbarHeight, setNavbarHeight] = useState(0);
- const navbarRef = useCallback(node => {
- if (node !== null) {
- setNavbarHeight(node.getBoundingClientRect().height);
- }
- }, []);
+
+ const {navbarRef, isNavbarVisible} = useHideableNavbar(hideOnScroll);
const showSidebar = useCallback(() => {
setSidebarShown(true);
@@ -71,36 +66,6 @@ function Navbar() {
[setTheme],
);
- const handleScroll = () => {
- const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
- const documentHeight = document.documentElement.scrollHeight - navbarHeight;
- const windowHeight = window.innerHeight;
-
- if (scrollTop < navbarHeight) {
- return;
- }
-
- if (lastScrollTop && scrollTop > lastScrollTop) {
- setIsNavbarVisible(false);
- } else if (scrollTop + windowHeight < documentHeight) {
- setIsNavbarVisible(true);
- }
-
- setLastScrollTop(scrollTop);
- };
-
- useEffect(() => {
- if (!hideOnScroll) {
- return undefined;
- }
-
- window.addEventListener('scroll', handleScroll);
-
- return () => {
- window.removeEventListener('scroll', handleScroll);
- };
- }, [lastScrollTop, navbarHeight]);
-
const logoUrl = useBaseUrl(logo.src);
return (
<>
@@ -112,8 +77,8 @@ function Navbar() {
ref={navbarRef}
className={classnames('navbar', 'navbar--light', 'navbar--fixed-top', {
'navbar-sidebar--show': sidebarShown,
- [styles.navbarHidable]: hideOnScroll,
- [styles.navbarHided]: !isNavbarVisible,
+ [styles.navbarHideable]: hideOnScroll,
+ [styles.navbarHidden]: !isNavbarVisible,
})}>
diff --git a/packages/docusaurus-theme-classic/src/theme/Navbar/styles.module.css b/packages/docusaurus-theme-classic/src/theme/Navbar/styles.module.css
index c06bb8b1aa2e..2d7f71ff8c98 100644
--- a/packages/docusaurus-theme-classic/src/theme/Navbar/styles.module.css
+++ b/packages/docusaurus-theme-classic/src/theme/Navbar/styles.module.css
@@ -17,10 +17,10 @@
}
}
-.navbarHidable {
+.navbarHideable {
transition: top 0.2s ease-in-out;
}
-.navbarHided {
+.navbarHidden {
top: calc(var(--ifm-navbar-height) * -1) !important;
}
diff --git a/packages/docusaurus-theme-classic/src/theme/hooks/useHideableNavbar.js b/packages/docusaurus-theme-classic/src/theme/hooks/useHideableNavbar.js
new file mode 100644
index 000000000000..a1941d1e5bee
--- /dev/null
+++ b/packages/docusaurus-theme-classic/src/theme/hooks/useHideableNavbar.js
@@ -0,0 +1,56 @@
+/**
+ * Copyright (c) 2017-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import React, {useState, useCallback, useEffect} from 'react'; // eslint-disable-line no-unused-vars
+
+const useHideableNavbar = hideOnScroll => {
+ const [isNavbarVisible, setIsNavbarVisible] = useState(true);
+ const [lastScrollTop, setLastScrollTop] = useState(0);
+ const [navbarHeight, setNavbarHeight] = useState(0);
+ const navbarRef = useCallback(node => {
+ if (node !== null) {
+ setNavbarHeight(node.getBoundingClientRect().height);
+ }
+ }, []);
+
+ const handleScroll = () => {
+ const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
+ const documentHeight = document.documentElement.scrollHeight - navbarHeight;
+ const windowHeight = window.innerHeight;
+
+ if (scrollTop < navbarHeight) {
+ return;
+ }
+
+ if (lastScrollTop && scrollTop > lastScrollTop) {
+ setIsNavbarVisible(false);
+ } else if (scrollTop + windowHeight < documentHeight) {
+ setIsNavbarVisible(true);
+ }
+
+ setLastScrollTop(scrollTop);
+ };
+
+ useEffect(() => {
+ if (!hideOnScroll) {
+ return undefined;
+ }
+
+ window.addEventListener('scroll', handleScroll);
+
+ return () => {
+ window.removeEventListener('scroll', handleScroll);
+ };
+ }, [lastScrollTop, navbarHeight]);
+
+ return {
+ navbarRef,
+ isNavbarVisible,
+ };
+};
+
+export default useHideableNavbar;
diff --git a/website/docs/theme-classic.md b/website/docs/theme-classic.md
index f1bf6352368b..0fb981b2b362 100644
--- a/website/docs/theme-classic.md
+++ b/website/docs/theme-classic.md
@@ -90,6 +90,23 @@ module.exports = {
Outbound links automatically get `target="_blank" rel="noopener noreferrer"`.
+### Auto-hide sticky navbar
+
+You can enable this cool UI feature that automatically hides the navbar when a user starts scrolling down the page, and show it again when the user scrolls up.
+
+```js
+// docusaurus/config.js
+module.exports = {
+ ...
+ themeConfig: {
+ navbar: {
+ hideOnScroll: true,
+ },
+ ...
+ },
+}
+```
+
## Footer
## `CodeBlock`