-
- {frontmatter.title}
-
+
+
+ {frontmatter.title}
+
+
{content}
diff --git a/src/app/[lang]/posts/page.tsx b/src/app/[lang]/posts/page.tsx
index 4a5f7b2..a23283c 100644
--- a/src/app/[lang]/posts/page.tsx
+++ b/src/app/[lang]/posts/page.tsx
@@ -24,10 +24,12 @@ async function RootPage({ params: { lang } }: RootParams) {
return (
-
- {metadata.title}
-
- {metadata.description}
+
+
+ {metadata.title}
+
+
{metadata.description}
+
);
diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx
index 9950f28..1ea5910 100644
--- a/src/components/Header/Header.tsx
+++ b/src/components/Header/Header.tsx
@@ -1,55 +1,76 @@
'use client';
+import type { StaticImport } from 'next/dist/shared/lib/get-img-props';
import { usePathname } from 'next/navigation';
-import { CSSProperties, useEffect, useState, useRef } from 'react';
+import { CSSProperties, useEffect, useRef } from 'react';
import useScroll from '@/hooks/useScroll';
import cn from '@/utils/cn';
import getLocale from '@/utils/getLocale';
import { clamp } from '@/utils/math';
-import ThemeSwitcher from '../ThemeSwitcher';
-import Link from '../Link';
-import Logo, { LogoProps } from './Logo';
import Container from '../Container';
+import Image from '../Image';
+import Link from '../Link';
+import ThemeSwitcher from '../ThemeSwitcher';
type MenuItem = {
text: string;
href: LinkWithoutLocalePathProps['href'];
};
+type Avatar = {
+ src: string | StaticImport;
+ alt: string;
+};
+
export type HeaderProps = {
- logo: Omit;
+ avatar: Avatar;
menu: MenuItem[];
+ scrollThreshold?: number;
};
-function Header({ logo, menu }: HeaderProps) {
+function Header({ avatar, menu, scrollThreshold = 100 }: HeaderProps) {
const pathname = usePathname();
- const [translateY, setTranslateY] = useState(0);
const [scroll] = useScroll();
const headerRef = useRef(null);
- const headerHeight = useRef(0);
+ const headerTranslateY = useRef(0);
const previousScrollY = useRef(0);
const currentScrollY = Math.floor(scroll.y);
- const scrollThreshold = 120;
-
- useEffect(() => {
- headerHeight.current = headerRef.current?.clientHeight || 0;
- }, []);
-
- useEffect(() => {
+ const avatarTranslateY = (
+ clamp(currentScrollY * -1, scrollThreshold * -1) + scrollThreshold
+ ).toFixed(2);
+ const avatarScale = (
+ 1.5 -
+ clamp(currentScrollY, scrollThreshold) / (scrollThreshold * 2)
+ ).toFixed(2);
+
+ const calcHeaderTranslateY = () => {
const scrollY = currentScrollY - scrollThreshold;
- if (scrollY <= 0) return setTranslateY(0);
+ if (scrollY <= 0) return headerTranslateY.current = 0;
const deltaScrollY = previousScrollY.current - scrollY;
+ const headerHeight = headerRef.current?.clientHeight || 0;
+ const newHeaderTranslateY = clamp(
+ headerTranslateY.current + deltaScrollY,
+ headerHeight * -1
+ );
previousScrollY.current = scrollY;
+ headerTranslateY.current = newHeaderTranslateY;
- setTranslateY((pre) =>
- clamp(pre + deltaScrollY, headerHeight.current * -1)
- );
- }, [currentScrollY]);
+ return newHeaderTranslateY;
+ }
+
+ const headerStyles = {
+ '--header-translate-y': `${calcHeaderTranslateY()}px`,
+ } as CSSProperties;
+
+ const avatarStyles = {
+ '--avatar-translate-y': `${avatarTranslateY}px`,
+ '--avatar-scale': avatarScale,
+ } as CSSProperties;
const isActiveLink = (href: LinkWithoutLocalePathProps['href']) => {
const locale = getLocale(pathname) || '';
@@ -65,14 +86,23 @@ function Header({ logo, menu }: HeaderProps) {
-
+
+
+