mirror of
https://github.com/LukeHagar/better-auth.git
synced 2025-12-09 12:27:43 +00:00
fix(docs): anchor link scrolling with conflict prevention (#5186)
This commit is contained in:
committed by
GitHub
parent
72d63b6e0f
commit
91778b5bbd
@@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
:root {
|
:root {
|
||||||
--fd-nav-height: 56px;
|
--fd-nav-height: 56px;
|
||||||
|
--fd-banner-height: 0px;
|
||||||
|
--fd-tocnav-height: 0px;
|
||||||
|
|
||||||
--background: oklch(1 0 0);
|
--background: oklch(1 0 0);
|
||||||
|
|
||||||
@@ -255,15 +257,19 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
scroll-behavior: smooth;
|
scroll-behavior: auto;
|
||||||
scroll-padding-top: calc(
|
scroll-padding-top: calc(
|
||||||
var(--fd-nav-height) +
|
var(--fd-nav-height, 56px) +
|
||||||
var(--fd-banner-height, 0px) +
|
var(--fd-banner-height, 0px) +
|
||||||
var(--fd-tocnav-height, 0px) +
|
var(--fd-tocnav-height, 0px) +
|
||||||
16px
|
24px
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html:not([data-anchor-scrolling]) {
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
|
||||||
/* Global, accessible custom scrollbars */
|
/* Global, accessible custom scrollbars */
|
||||||
* {
|
* {
|
||||||
scrollbar-width: thin; /* Firefox */
|
scrollbar-width: thin; /* Firefox */
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { Analytics } from "@vercel/analytics/react";
|
|||||||
import { ThemeProvider } from "@/components/theme-provider";
|
import { ThemeProvider } from "@/components/theme-provider";
|
||||||
import { Toaster } from "@/components/ui/sonner";
|
import { Toaster } from "@/components/ui/sonner";
|
||||||
import { CustomSearchDialog } from "@/components/search-dialog";
|
import { CustomSearchDialog } from "@/components/search-dialog";
|
||||||
|
import { AnchorScroll } from "@/components/anchor-scroll-fix";
|
||||||
|
|
||||||
export const metadata = createMetadata({
|
export const metadata = createMetadata({
|
||||||
title: {
|
title: {
|
||||||
@@ -58,6 +59,7 @@ export default function Layout({ children }: { children: ReactNode }) {
|
|||||||
: undefined,
|
: undefined,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<AnchorScroll />
|
||||||
<NavbarProvider>
|
<NavbarProvider>
|
||||||
<Navbar />
|
<Navbar />
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
107
docs/components/anchor-scroll-fix.tsx
Normal file
107
docs/components/anchor-scroll-fix.tsx
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
|
export function AnchorScroll() {
|
||||||
|
const scrollTimeoutRef = useRef<NodeJS.Timeout | undefined>(undefined);
|
||||||
|
const isScrollingRef = useRef(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
function calculateScrollOffset() {
|
||||||
|
const root = document.documentElement;
|
||||||
|
const navHeight = parseInt(
|
||||||
|
getComputedStyle(root).getPropertyValue("--fd-nav-height") || "56",
|
||||||
|
);
|
||||||
|
const bannerHeight = parseInt(
|
||||||
|
getComputedStyle(root).getPropertyValue("--fd-banner-height") || "0",
|
||||||
|
);
|
||||||
|
const tocnavHeight = parseInt(
|
||||||
|
getComputedStyle(root).getPropertyValue("--fd-tocnav-height") || "0",
|
||||||
|
);
|
||||||
|
|
||||||
|
return navHeight + bannerHeight + tocnavHeight + 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
function smoothScrollToElement(element: HTMLElement) {
|
||||||
|
if (isScrollingRef.current) return;
|
||||||
|
|
||||||
|
isScrollingRef.current = true;
|
||||||
|
document.documentElement.setAttribute("data-anchor-scrolling", "true");
|
||||||
|
|
||||||
|
const elementRect = element.getBoundingClientRect();
|
||||||
|
const scrollOffset = calculateScrollOffset();
|
||||||
|
const targetPosition =
|
||||||
|
window.pageYOffset + elementRect.top - scrollOffset;
|
||||||
|
|
||||||
|
// Simple smooth scroll animation
|
||||||
|
const startPosition = window.pageYOffset;
|
||||||
|
const distance = targetPosition - startPosition;
|
||||||
|
const duration = Math.min(500, Math.abs(distance) * 0.3);
|
||||||
|
const startTime = performance.now();
|
||||||
|
|
||||||
|
function animateScroll(currentTime: number) {
|
||||||
|
const elapsed = currentTime - startTime;
|
||||||
|
const progress = Math.min(elapsed / duration, 1);
|
||||||
|
const easeOutCubic = (t: number) => 1 - Math.pow(1 - t, 3);
|
||||||
|
const currentPosition =
|
||||||
|
startPosition + distance * easeOutCubic(progress);
|
||||||
|
|
||||||
|
window.scrollTo(0, currentPosition);
|
||||||
|
|
||||||
|
if (progress < 1) {
|
||||||
|
requestAnimationFrame(animateScroll);
|
||||||
|
} else {
|
||||||
|
document.documentElement.removeAttribute("data-anchor-scrolling");
|
||||||
|
isScrollingRef.current = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
requestAnimationFrame(animateScroll);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAnchorScroll() {
|
||||||
|
if (window.location.hash) {
|
||||||
|
const element = document.getElementById(window.location.hash.slice(1));
|
||||||
|
if (element) {
|
||||||
|
scrollTimeoutRef.current = setTimeout(
|
||||||
|
() => smoothScrollToElement(element),
|
||||||
|
100,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleHashChange() {
|
||||||
|
const element = document.getElementById(window.location.hash.slice(1));
|
||||||
|
if (element) smoothScrollToElement(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAnchorClick(event: Event) {
|
||||||
|
const link = (event.target as HTMLElement).closest(
|
||||||
|
'a[href^="#"]',
|
||||||
|
) as HTMLAnchorElement;
|
||||||
|
|
||||||
|
if (link?.hash) {
|
||||||
|
event.preventDefault();
|
||||||
|
const element = document.getElementById(link.hash.slice(1));
|
||||||
|
|
||||||
|
if (element) {
|
||||||
|
history.pushState(null, "", link.hash);
|
||||||
|
smoothScrollToElement(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleAnchorScroll();
|
||||||
|
window.addEventListener("hashchange", handleHashChange);
|
||||||
|
document.addEventListener("click", handleAnchorClick);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (scrollTimeoutRef.current) clearTimeout(scrollTimeoutRef.current);
|
||||||
|
window.removeEventListener("hashchange", handleHashChange);
|
||||||
|
document.removeEventListener("click", handleAnchorClick);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
@@ -48,11 +48,14 @@ export function NavProvider({
|
|||||||
if (transparentMode !== "top") return;
|
if (transparentMode !== "top") return;
|
||||||
|
|
||||||
const listener = () => {
|
const listener = () => {
|
||||||
|
if (document.documentElement.hasAttribute("data-anchor-scrolling")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
setTransparent(window.scrollY < 10);
|
setTransparent(window.scrollY < 10);
|
||||||
};
|
};
|
||||||
|
|
||||||
listener();
|
listener();
|
||||||
window.addEventListener("scroll", listener);
|
window.addEventListener("scroll", listener, { passive: true });
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener("scroll", listener);
|
window.removeEventListener("scroll", listener);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user