Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 28 additions & 2 deletions client/src/components/header.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useContext, useEffect, useState } from "react";
import { useContext, useEffect, useRef, useState } from "react";
import { ProfileContext } from "../state/profile";
import { Padding } from "./padding";
import { useSiteConfig } from "../hooks/useSiteConfig";
Expand All @@ -13,6 +13,7 @@ export function Header({ children }: { children?: React.ReactNode }) {
const layoutDefinition = getHeaderLayoutDefinition(headerLayout);
const [isRevealed, setIsRevealed] = useState(true);
const [isAtTop, setIsAtTop] = useState(true);
const headerRef = useRef<HTMLDivElement | null>(null);

useEffect(() => {
let lastScrollY = window.scrollY;
Expand All @@ -33,6 +34,31 @@ export function Header({ children }: { children?: React.ReactNode }) {
};
}, [headerBehavior]);

useEffect(() => {
const root = document.documentElement;
const setHeaderScrollOffset = () => {
const headerHeight = headerRef.current?.getBoundingClientRect().height ?? 0;
root.style.setProperty("--header-scroll-offset", `${Math.ceil(headerHeight + 16)}px`);
Comment thread
OXeu marked this conversation as resolved.
};

setHeaderScrollOffset();

const resizeObserver = new ResizeObserver(() => {
setHeaderScrollOffset();
});

if (headerRef.current) {
resizeObserver.observe(headerRef.current);
}

window.addEventListener("resize", setHeaderScrollOffset);

return () => {
resizeObserver.disconnect();
window.removeEventListener("resize", setHeaderScrollOffset);
};
}, [headerBehavior, headerLayout]);

const useTopHeader = layoutDefinition.kind === "top";
const headerPaddingClassName = headerLayout === "compact" ? "mx-0 mt-0" : "mx-4 mt-4";
const containerClassName =
Expand All @@ -48,7 +74,7 @@ export function Header({ children }: { children?: React.ReactNode }) {
{headerLayout === "compact" ? (
<div className="pointer-events-none fixed inset-x-0 top-0 -z-10 h-64 bg-gradient-to-b from-theme/15 to-white/0 dark:from-theme/20 dark:to-transparent" />
) : null}
<div className={containerClassName}>
<div ref={headerRef} className={containerClassName}>
<div className="w-screen">
{headerLayout === "compact" ? (
<div className="w-full">
Expand Down
18 changes: 12 additions & 6 deletions client/src/components/markdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -298,8 +298,9 @@ export function Markdown({ content }: { content: string }) {
return (
<h1
id={children?.toString()}
className="text-3xl font-bold mt-4"
{...props}
className="text-3xl font-bold mt-4"
style={{ ...props.style, scrollMarginTop: "var(--header-scroll-offset, 7rem)" }}
>
{children}
</h1>
Expand All @@ -309,8 +310,9 @@ export function Markdown({ content }: { content: string }) {
return (
<h2
id={children?.toString()}
className="text-2xl font-bold mt-4"
{...props}
className="text-2xl font-bold mt-4"
style={{ ...props.style, scrollMarginTop: "var(--header-scroll-offset, 7rem)" }}
Comment thread
OXeu marked this conversation as resolved.
Outdated
>
{children}
</h2>
Expand All @@ -320,8 +322,9 @@ export function Markdown({ content }: { content: string }) {
return (
<h3
id={children?.toString()}
className="text-xl font-bold mt-4"
{...props}
className="text-xl font-bold mt-4"
style={{ ...props.style, scrollMarginTop: "var(--header-scroll-offset, 7rem)" }}
>
{children}
</h3>
Expand All @@ -331,8 +334,9 @@ export function Markdown({ content }: { content: string }) {
return (
<h4
id={children?.toString()}
className="text-lg font-bold mt-4"
{...props}
className="text-lg font-bold mt-4"
style={{ ...props.style, scrollMarginTop: "var(--header-scroll-offset, 7rem)" }}
>
{children}
</h4>
Expand All @@ -342,8 +346,9 @@ export function Markdown({ content }: { content: string }) {
return (
<h5
id={children?.toString()}
className="text-base font-bold mt-4"
{...props}
className="text-base font-bold mt-4"
style={{ ...props.style, scrollMarginTop: "var(--header-scroll-offset, 7rem)" }}
>
{children}
</h5>
Expand All @@ -353,8 +358,9 @@ export function Markdown({ content }: { content: string }) {
return (
<h6
id={children?.toString()}
className="text-sm font-bold mt-4"
{...props}
className="text-sm font-bold mt-4"
style={{ ...props.style, scrollMarginTop: "var(--header-scroll-offset, 7rem)" }}
>
{children}
</h6>
Expand Down
14 changes: 12 additions & 2 deletions client/src/hooks/useTableOfContents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ export interface TableOfContent {
element: HTMLElement
}

const getHeaderScrollOffset = () => {
const rawValue = getComputedStyle(document.documentElement)
.getPropertyValue('--header-scroll-offset')
.trim()
const offset = Number.parseFloat(rawValue)
return Number.isFinite(offset) ? offset : 0
}

const useTableOfContents = (selector: string) => {
const intersectingListRef = useRef<boolean[]>([]) // isIntersecting array
const [tableOfContents, setTableOfContents] = useState<TableOfContent[]>([])
Expand Down Expand Up @@ -86,9 +94,11 @@ const useTableOfContents = (selector: string) => {
className={`cursor-pointer hover:opacity-50 ${activeIndex === item.index ? "text-theme" : ""}`}
style={{ marginLeft: item.marginLeft }}
onClick={() => {
item.element.scrollIntoView({
const top = item.element.getBoundingClientRect().top + window.scrollY - getHeaderScrollOffset()
window.scrollTo({
top: Math.max(top, 0),
behavior: 'smooth'
});
})
}}
>
{item.text}
Expand Down
Loading