forked from UKSOURCE/ipv6
init
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
import Link from "next/link";
|
||||
import siteData from "@/app/data/site.json";
|
||||
|
||||
export default function Footer() {
|
||||
return (
|
||||
<footer className="w-full bg-surface-container-low border-t border-white/5 py-12 lg:py-20">
|
||||
<div className="flex flex-col xl:flex-row justify-between items-center px-[var(--spacing-gutter)] gap-12 w-full max-w-hd mx-auto">
|
||||
<div className="font-[var(--font-label-caps)] text-xl lg:text-[24px] text-primary tracking-[0.4em] font-bold uppercase">
|
||||
{siteData.brand.name}
|
||||
</div>
|
||||
<div className="flex flex-wrap justify-center gap-8 lg:gap-16">
|
||||
{siteData.footer.links.map((l) => (
|
||||
<Link
|
||||
key={l.label}
|
||||
className="text-on-surface-variant font-[var(--font-label-caps)] text-xs lg:text-sm hover:text-primary transition-colors"
|
||||
href={l.href}
|
||||
>
|
||||
{l.label}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
<div className="text-[14px] lg:text-[16px] text-on-surface-variant/40 text-center xl:text-right max-w-sm leading-relaxed uppercase tracking-widest">
|
||||
{siteData.footer.copyright}
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import HeaderClient from "./HeaderClient";
|
||||
import siteData from "@/app/data/site.json";
|
||||
|
||||
export default function Header() {
|
||||
return <HeaderClient brand={siteData.brand} nav={siteData.nav} />;
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
|
||||
type NavItem = { label: string; href: string };
|
||||
|
||||
export default function HeaderClient({
|
||||
brand,
|
||||
nav,
|
||||
}: {
|
||||
brand: { name: string };
|
||||
nav: NavItem[];
|
||||
}) {
|
||||
const pathname = usePathname();
|
||||
const [mobileOpen, setMobileOpen] = useState(false);
|
||||
|
||||
const isHome = pathname === "/";
|
||||
const navItems = useMemo(() => nav.filter((x) => x.href !== "/"), [nav]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!mobileOpen) return;
|
||||
document.body.style.overflow = "hidden";
|
||||
return () => {
|
||||
document.body.style.overflow = "";
|
||||
};
|
||||
}, [mobileOpen]);
|
||||
|
||||
return (
|
||||
<header className="fixed top-0 w-full z-50 bg-surface/80 backdrop-blur-xl border-b border-white/5">
|
||||
<div className="flex justify-between items-center px-[var(--spacing-gutter)] py-4 lg:py-8 w-full max-w-hd mx-auto">
|
||||
<Link
|
||||
href="/"
|
||||
className="font-[var(--font-display-lg)] text-[20px] sm:text-[22px] lg:text-[36px] text-primary tracking-tighter uppercase font-bold"
|
||||
>
|
||||
{brand.name}
|
||||
</Link>
|
||||
|
||||
<nav className="hidden xl:flex items-center gap-10 2xl:gap-16">
|
||||
<Link
|
||||
className={`font-[var(--font-label-caps)] text-sm ${
|
||||
isHome ? "text-primary border-b-2 border-primary pb-1" : "text-on-surface-variant hover:text-primary"
|
||||
} transition-colors duration-300`}
|
||||
href="/"
|
||||
>
|
||||
Home
|
||||
</Link>
|
||||
{navItems.map((item) => (
|
||||
<Link
|
||||
key={item.href}
|
||||
className="text-on-surface-variant font-[var(--font-label-caps)] text-sm hover:text-primary transition-colors duration-300"
|
||||
href={item.href}
|
||||
>
|
||||
{item.label}
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
<div className="flex items-center gap-4 lg:gap-10">
|
||||
<span className="hidden sm:block text-on-surface-variant/60 font-[var(--font-label-caps)] text-xs">
|
||||
EN
|
||||
</span>
|
||||
<Link
|
||||
href="/registration"
|
||||
className="hidden sm:inline-flex gold-gradient-bg text-on-primary px-6 lg:px-10 py-3 lg:py-4 font-[var(--font-body-base)] text-sm lg:text-[18px] font-bold uppercase tracking-wider rounded-lg active:scale-95 transition-transform shadow-lg shadow-primary/10"
|
||||
>
|
||||
Register
|
||||
</Link>
|
||||
<button
|
||||
type="button"
|
||||
className="xl:hidden text-primary p-2"
|
||||
aria-label="Toggle menu"
|
||||
aria-expanded={mobileOpen}
|
||||
onClick={() => setMobileOpen((v) => !v)}
|
||||
>
|
||||
<span className="material-symbols-outlined text-3xl">{mobileOpen ? "close" : "menu"}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={`fixed inset-0 top-[72px] lg:top-[104px] bg-surface z-40 flex-col p-[var(--spacing-gutter)] gap-8 xl:hidden border-t border-white/5 ${
|
||||
mobileOpen ? "flex" : "hidden"
|
||||
}`}
|
||||
>
|
||||
<Link className="text-2xl font-[var(--font-display-lg)] text-on-surface" href="/" onClick={() => setMobileOpen(false)}>
|
||||
Home
|
||||
</Link>
|
||||
{navItems.map((item) => (
|
||||
<Link
|
||||
key={item.href}
|
||||
className="text-2xl font-[var(--font-display-lg)] text-on-surface"
|
||||
href={item.href}
|
||||
onClick={() => setMobileOpen(false)}
|
||||
>
|
||||
{item.label}
|
||||
</Link>
|
||||
))}
|
||||
<div className="mt-auto flex flex-col gap-6">
|
||||
<div className="text-on-surface-variant/60 font-[var(--font-label-caps)] text-sm">Language: EN</div>
|
||||
<Link
|
||||
href="/registration"
|
||||
className="gold-gradient-bg text-on-primary w-full py-5 font-[var(--font-body-base)] text-lg font-bold uppercase tracking-wider rounded-lg text-center"
|
||||
onClick={() => setMobileOpen(false)}
|
||||
>
|
||||
Register Now
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user