179 lines
7.5 KiB
TypeScript
179 lines
7.5 KiB
TypeScript
import Head from "next/head";
|
|
import Link from "next/link";
|
|
import { useRouter } from "next/router";
|
|
import { useState } from "react";
|
|
import styles from "@/styles/Layout.module.css";
|
|
|
|
export interface SiteLayoutProps {
|
|
title?: string;
|
|
description?: string;
|
|
keywords?: string;
|
|
canonical?: string;
|
|
children: React.ReactNode;
|
|
}
|
|
|
|
const WORLD_LINKS = [
|
|
{ href: "/world/lore", label: "Lore" },
|
|
{ href: "/world/world", label: "World" },
|
|
{ href: "/world/characters", label: "Characters" },
|
|
{ href: "/world/music", label: "Music" },
|
|
{ href: "/world/gallery", label: "Gallery" },
|
|
];
|
|
|
|
export default function SiteLayout({
|
|
title = "Bone Lord Bob — American Anime",
|
|
description = "Bone Lord Bob is an original American Anime series — dark, brutal, beautiful.",
|
|
keywords = "Bone Lord Bob, American Anime, indie animation, dark fantasy",
|
|
canonical,
|
|
children,
|
|
}: SiteLayoutProps) {
|
|
const router = useRouter();
|
|
const [worldOpen, setWorldOpen] = useState(false);
|
|
const [mobileOpen, setMobileOpen] = useState(false);
|
|
|
|
const canonicalUrl = canonical
|
|
? `https://www.bonelordbob.com${canonical}`
|
|
: `https://www.bonelordbob.com${router.asPath.split("?")[0]}`;
|
|
|
|
const isActive = (href: string) =>
|
|
href === "/" ? router.pathname === "/" : router.pathname.startsWith(href);
|
|
|
|
const isWorldActive = WORLD_LINKS.some((l) => router.pathname.startsWith(l.href));
|
|
|
|
return (
|
|
<>
|
|
<Head>
|
|
<title>{title === "Bone Lord Bob — American Anime" ? title : `${title} | Bone Lord Bob`}</title>
|
|
<meta name="description" content={description} />
|
|
<meta name="keywords" content={keywords} />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<link rel="canonical" href={canonicalUrl} />
|
|
<meta property="og:title" content={title} />
|
|
<meta property="og:description" content={description} />
|
|
<meta property="og:url" content={canonicalUrl} />
|
|
<meta property="og:image" content="https://www.bonelordbob.com/og-image.png" />
|
|
<meta name="twitter:card" content="summary_large_image" />
|
|
<meta name="twitter:title" content={title} />
|
|
<meta name="twitter:description" content={description} />
|
|
</Head>
|
|
|
|
<div className={styles.site}>
|
|
{/* ── Nav ── */}
|
|
<nav className={styles.nav}>
|
|
<Link href="/" className={styles.navLogo}>
|
|
Bone Lord <span>Bob</span>
|
|
</Link>
|
|
|
|
{/* Desktop nav */}
|
|
<ul className={styles.navLinks}>
|
|
<li className={styles.navItem}>
|
|
<Link
|
|
href="/"
|
|
className={`${styles.navLink} ${isActive("/") ? styles.navLinkActive : ""}`}
|
|
>
|
|
Home
|
|
</Link>
|
|
</li>
|
|
|
|
{/* World dropdown */}
|
|
<li
|
|
className={styles.navItem}
|
|
onMouseEnter={() => setWorldOpen(true)}
|
|
onMouseLeave={() => setWorldOpen(false)}
|
|
>
|
|
<button
|
|
className={`${styles.navLink} ${styles.navDropdownTrigger} ${isWorldActive ? styles.navLinkActive : ""}`}
|
|
aria-haspopup="true"
|
|
aria-expanded={worldOpen}
|
|
>
|
|
World <span className={styles.chevron}>▾</span>
|
|
</button>
|
|
{worldOpen && (
|
|
<div className={styles.dropdown}>
|
|
{WORLD_LINKS.map((l) => (
|
|
<Link
|
|
key={l.href}
|
|
href={l.href}
|
|
className={`${styles.dropdownItem} ${router.pathname === l.href ? styles.dropdownItemActive : ""}`}
|
|
onClick={() => setWorldOpen(false)}
|
|
>
|
|
{l.label}
|
|
</Link>
|
|
))}
|
|
</div>
|
|
)}
|
|
</li>
|
|
|
|
<li className={styles.navItem}>
|
|
<Link href="/#follow" className={styles.navLink}>
|
|
Follow
|
|
</Link>
|
|
</li>
|
|
</ul>
|
|
|
|
{/* Mobile hamburger */}
|
|
<button
|
|
className={styles.hamburger}
|
|
onClick={() => setMobileOpen(!mobileOpen)}
|
|
aria-label="Toggle menu"
|
|
>
|
|
<span /><span /><span />
|
|
</button>
|
|
</nav>
|
|
|
|
{/* Mobile drawer */}
|
|
{mobileOpen && (
|
|
<div className={styles.mobileDrawer}>
|
|
<Link href="/" className={styles.mobileLink} onClick={() => setMobileOpen(false)}>Home</Link>
|
|
<div className={styles.mobileSectionLabel}>World</div>
|
|
{WORLD_LINKS.map((l) => (
|
|
<Link key={l.href} href={l.href} className={styles.mobileLinkIndent} onClick={() => setMobileOpen(false)}>
|
|
{l.label}
|
|
</Link>
|
|
))}
|
|
<Link href="/#follow" className={styles.mobileLink} onClick={() => setMobileOpen(false)}>Follow</Link>
|
|
</div>
|
|
)}
|
|
|
|
{/* ── Page content ── */}
|
|
<main className={styles.main}>{children}</main>
|
|
|
|
{/* ── Footer ── */}
|
|
<footer className={styles.footer}>
|
|
<div className={styles.footerTop}>
|
|
<div className={styles.footerBrand}>
|
|
<span className={styles.footerLogo}>Bone Lord <span>Bob</span></span>
|
|
<p className={styles.footerTagline}>American Anime. Dark. Brutal. Beautiful.</p>
|
|
</div>
|
|
<div className={styles.footerColumns}>
|
|
<div className={styles.footerCol}>
|
|
<span className={styles.footerColTitle}>World</span>
|
|
{WORLD_LINKS.map((l) => (
|
|
<Link key={l.href} href={l.href} className={styles.footerLink}>{l.label}</Link>
|
|
))}
|
|
</div>
|
|
<div className={styles.footerCol}>
|
|
<span className={styles.footerColTitle}>Follow</span>
|
|
<a href="https://www.youtube.com/@bonelordbob" target="_blank" rel="noopener noreferrer" className={styles.footerLink}>YouTube</a>
|
|
<a href="https://x.com/bonelordbob" target="_blank" rel="noopener noreferrer" className={styles.footerLink}>X / Twitter</a>
|
|
<a href="https://www.instagram.com/bonelordbob" target="_blank" rel="noopener noreferrer" className={styles.footerLink}>Instagram</a>
|
|
<a href="https://www.facebook.com/profile.php?id=61580718487639" target="_blank" rel="noopener noreferrer" className={styles.footerLink}>Facebook</a>
|
|
<a href="https://www.tiktok.com/@bonelordbob" target="_blank" rel="noopener noreferrer" className={styles.footerLink}>TikTok</a>
|
|
<a href="https://www.patreon.com/BoneLordBob" target="_blank" rel="noopener noreferrer" className={styles.footerLink}>Patreon</a>
|
|
</div>
|
|
<div className={styles.footerCol}>
|
|
<span className={styles.footerColTitle}>Legal</span>
|
|
<Link href="/privacy" className={styles.footerLink}>Privacy Policy</Link>
|
|
<Link href="/terms" className={styles.footerLink}>Terms & Conditions</Link>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className={styles.footerBottom}>
|
|
<span>© {new Date().getFullYear()} Mathison Projects Inc. All rights reserved. · Powered by <a href="https://www.silmaai.com" target="_blank" rel="noopener noreferrer" style={{ color: "#e8dcc8", textDecoration: "underline", opacity: 0.7 }}>Silma AI</a></span>
|
|
</div>
|
|
</footer>
|
|
</div>
|
|
</>
|
|
);
|
|
}
|