diff --git a/src/components/MoveToTopButton/index.astro b/src/components/MoveToTopButton/index.astro new file mode 100644 index 0000000000..39d05ff097 --- /dev/null +++ b/src/components/MoveToTopButton/index.astro @@ -0,0 +1,37 @@ +--- +const { class: customClass = "" } = Astro.props; +--- + +<a + id="moveToTop" + class={`move-to-top hidden outline outline-1 outline-type-color py-2 px-4 flex flex-nowrap w-fit items-center justify-center rounded-full hover:bg-sidebar-type-color hover:text-bg-color hover:no-underline ${customClass}`} + href="#" + title="Go to top" +> + <span>↑</span> +</a> + +<script> + const moveToTopButton = document.getElementById("moveToTop"); + + // Ensure the element exists before accessing its methods + if (moveToTopButton) { + // Show the button when the user scrolls 100px down from the top + window.onscroll = function () { + if (document.body.scrollTop > 100 || document.documentElement.scrollTop > 100) { + moveToTopButton.classList.add("visible"); + moveToTopButton.classList.remove("hidden"); + } else { + moveToTopButton.classList.add("hidden"); + moveToTopButton.classList.remove("visible"); + } + }; + + // Scroll smoothly to the top when the button is clicked + moveToTopButton.addEventListener("click", (event) => { + event.preventDefault(); // Prevent default anchor behavior + window.scrollTo({ top: 0, behavior: "smooth" }); + }); + } +</script> + diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro index e711c57b2a..51ccf0d912 100644 --- a/src/layouts/BaseLayout.astro +++ b/src/layouts/BaseLayout.astro @@ -11,6 +11,8 @@ import "@styles/base.scss"; import type { CollectionEntry } from "astro:content"; import { getCollectionInLocaleWithFallbacks } from "@pages/_utils"; import { removeLocalePrefix } from "@i18n/utils"; +import MoveToTopButton from "@components/MoveToTopButton/index.astro"; +import "@styles/move-to-top.scss"; interface Props { title: string; @@ -21,7 +23,6 @@ interface Props { variant?: "root" | "item" | "search" | "homepage"; topic?: PageTopic; mainContentParentClass?: string; - /* Only needed for the homepage */ homepageConfig?: CollectionEntry<"homepage">; } @@ -62,10 +63,7 @@ const headerTopic = topic : { name: t(capitalize(fallbackTopic)) as string, url: `/${fallbackTopic}` }; --- -<html - class={`${titleClass.toLowerCase()} ${className} scroll-smooth`} - lang={currentLocale} -> +<html class={`${titleClass.toLowerCase()} ${className} scroll-smooth`} lang={currentLocale}> <head> <script is:inline> // Get any theme settings and apply before load @@ -124,5 +122,6 @@ const headerTopic = topic </main> <Footer /> </div> + <MoveToTopButton /> </body> </html> diff --git a/styles/move-to-top.scss b/styles/move-to-top.scss new file mode 100644 index 0000000000..67bdb7bdf3 --- /dev/null +++ b/styles/move-to-top.scss @@ -0,0 +1,47 @@ +.move-to-top { + position: fixed; + bottom: 4.5rem; + right: 1.5rem; + padding: 0.75rem; + background-color: #000000; + color: #ffffff; + border-radius: 50%; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + opacity: 0; + transform: translateY(20px); + transition: opacity 0.3s ease, transform 0.3s ease; + z-index: 1000; + cursor: pointer; + font-size: 0.875rem; + } + + .move-to-top.hidden { + opacity: 0; + pointer-events: none; + } + + .move-to-top.visible { + opacity: 1; + transform: translateY(0); + } + + .move-to-top:hover { + background-color: #333333; + } + + .move-to-top:focus { + outline: none; + box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.5); + } + .move-to-top:active { + background-color: #1a1a1a; + } + @media screen and (max-width: 640px) { + .move-to-top { + bottom: 2rem; + right: 1rem; + padding: 0.5rem; + font-size: 0.75rem; + } + } + \ No newline at end of file