--- /** * BaseLayout.astro * --------------------------------------------------------------------------- * The shared shell for every page: , (fonts, meta, theme-init * inline script), the fixed NavBar, the main content slot, and the Footer. * * Theming axis #1 (light/dark) is set on by the inline * theme-init script before paint (no FOUC). Theming axis #2 (section tone) is * set on the via the `surface` prop so each page can opt * into neutral / professional / interest styling. * * Page transitions use Astro's (View Transitions API). */ import "@/styles/global.css"; // Self-hosted fonts (woff2, variable). Avoids external requests for perf + privacy. import "@fontsource-variable/inter"; import "@fontsource-variable/plus-jakarta-sans"; import "@fontsource-variable/jetbrains-mono"; import { ClientRouter } from "astro:transitions"; import NavBar from "@components/nav/NavBar.astro"; import StoreNav from "@components/store/StoreNav.astro"; import Footer from "@components/layout/Footer.astro"; import { themeInitScript } from "@/scripts/theme-init"; import { siteIdentity } from "@lib/site"; // Dev-only react-refresh preamble (see astro.config.mjs for why). Imported as a // raw string so we can emit it as the FIRST module script in , ahead of // any React island, so $RefreshReg$/$RefreshSig$ exist before islands eval. import { REACT_REFRESH_PREAMBLE } from "../../astro.config.mjs"; // Only emit the preamble in dev (import.meta.env.DEV). Production builds compile // islands to jsx/jsxs and never touch the refresh runtime. const isDev = import.meta.env.DEV; type Surface = "neutral" | "professional" | "interest"; interface Props { title?: string; description?: string; /** Which section tone to apply. Defaults to neutral (Home/About/Contact). */ surface?: Surface; /** Override default OG image path. */ ogImage?: string; /** When true, emit . Use on pages that shouldn't be indexed (e.g. /contact, to reduce scraping). */ noindex?: boolean; /** When true, render the store-only nav (StoreNav) instead of the main NavBar. Used by store pages, which intentionally share no links with the main site. */ storeNav?: boolean; } const { title, description = "Greg Kilmer is a software developer, maker, and tabletop gamemaster based in Philadelphia. This is where his professional and personal worlds meet.", surface = "neutral", ogImage, noindex = false, storeNav = false, } = Astro.props; // True on the Home page. Used to switch the NavBar's view-transition-name so // the Home hero can morph into / out of the nav bar (expand/collapse effect). const isHome = Astro.url.pathname === "/"; const fullTitle = title ? `${title} | ${siteIdentity.name}` : `${siteIdentity.name} — Developer. Maker. Storyteller.`; const canonical = new URL( Astro.url.pathname, Astro.site ?? "https://gregkilmer.com", ); const ogImageUrl = ogImage ? new URL(ogImage, Astro.site ?? "https://gregkilmer.com").toString() : new URL( "/images/hero-portrait.jpg", Astro.site ?? "https://gregkilmer.com", ).toString(); --- {/* Dev-only react-refresh preamble. MUST be the first module script so the refresh globals exist before any React island module evaluates. Without it, islands throw at eval time and never hydrate (see astro.config.mjs). */} {isDev && ( Skip to content {storeNav ? : }