diff --git a/src/components/ScrollNavigator.tsx b/src/components/ScrollNavigator.tsx
new file mode 100644
index 00000000..54470a42
--- /dev/null
+++ b/src/components/ScrollNavigator.tsx
@@ -0,0 +1,74 @@
+import { useEffect, useState } from "react";
+import { ChevronDown, ChevronUp } from "lucide-react";
+
+const BOTTOM_THRESHOLD = 24;
+
+const ScrollNavigator = () => {
+ const [showUpButton, setShowUpButton] = useState(false);
+ const [showDownButton, setShowDownButton] = useState(false);
+
+ const updateVisibility = () => {
+ const { documentElement } = document;
+ const scrollTop = window.scrollY;
+ const scrollableHeight =
+ documentElement.scrollHeight - documentElement.clientHeight;
+ const nearBottom = scrollTop >= scrollableHeight - BOTTOM_THRESHOLD;
+
+ setShowUpButton(scrollTop > 300);
+ setShowDownButton(scrollableHeight > 0 && !nearBottom);
+ };
+
+ const scrollToTop = () => {
+ window.scrollTo({ top: 0, behavior: "smooth" });
+ };
+
+ const scrollToBottom = () => {
+ window.scrollTo({
+ top: document.documentElement.scrollHeight,
+ behavior: "smooth",
+ });
+ };
+
+ useEffect(() => {
+ window.addEventListener("scroll", updateVisibility);
+ window.addEventListener("resize", updateVisibility);
+ updateVisibility();
+
+ return () => {
+ window.removeEventListener("scroll", updateVisibility);
+ window.removeEventListener("resize", updateVisibility);
+ };
+ }, []);
+
+ if (!showUpButton && !showDownButton) {
+ return null;
+ }
+
+ return (
+
+ {showUpButton && (
+
+ )}
+
+ {showDownButton && (
+
+ )}
+
+ );
+};
+
+export default ScrollNavigator;
\ No newline at end of file