1fce1b3cec
- UI atoms: Chip, Button, SplitCvButton, LangSwitch, ThemeToggle, Card, SectionLabel, TraceMotif, FramedImage - core: Base/Seo/Header/MobileNav/Footer/HintBar/SkipLink/ThemeScript - homepage sections: Hero, ProofStrip, SkillsGrid, ProjectCards, ExperienceTimeline, AboutTeaser, ContactBand + Homepage composition (EN/NO) - behaviour modules: theme, nav (overlay/hint/copy/header), observer (reveal/spy), lightbox, form — progressive enhancement, fail-silent - SEO head component: meta, hreflang from slug map, JSON-LD, OG references - move content data to src/data (avoid Astro reserved content-collection folder) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
68 lines
2.0 KiB
Plaintext
68 lines
2.0 KiB
Plaintext
---
|
|
import '@fontsource-variable/space-grotesk';
|
|
import '@fontsource-variable/inter';
|
|
import '@fontsource-variable/jetbrains-mono';
|
|
import '@styles/global.css';
|
|
|
|
import type { Locale } from '@i18n/locales';
|
|
import { HTML_LANG } from '@i18n/locales';
|
|
import type { PageId } from '@i18n/slugMap';
|
|
import Seo from '@components/core/Seo.astro';
|
|
import ThemeScript from '@components/core/ThemeScript.astro';
|
|
import SkipLink from '@components/core/SkipLink.astro';
|
|
import Header from '@components/core/Header.astro';
|
|
import HintBar from '@components/core/HintBar.astro';
|
|
import Footer from '@components/core/Footer.astro';
|
|
|
|
interface Props {
|
|
locale: Locale;
|
|
pageId: PageId;
|
|
title: string;
|
|
description: string;
|
|
ogType?: 'website' | 'profile' | 'article';
|
|
jsonLdOpts?: {
|
|
projectName?: string;
|
|
projectType?: 'SoftwareApplication' | 'SoftwareSourceCode';
|
|
};
|
|
mainClass?: string;
|
|
}
|
|
const { locale, pageId, title, description, ogType, jsonLdOpts, mainClass } = Astro.props;
|
|
---
|
|
|
|
<!doctype html>
|
|
<html lang={HTML_LANG[locale]} data-theme="dark">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<ThemeScript />
|
|
<Seo
|
|
locale={locale}
|
|
pageId={pageId}
|
|
title={title}
|
|
description={description}
|
|
ogType={ogType}
|
|
jsonLdOpts={jsonLdOpts}
|
|
/>
|
|
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
|
<meta name="theme-color" content="#0B0E14" />
|
|
<slot name="head" />
|
|
</head>
|
|
<body>
|
|
<div id="scroll-sentinel" aria-hidden="true" class="pointer-events-none absolute top-0 h-24 w-px"></div>
|
|
<SkipLink locale={locale} />
|
|
<Header locale={locale} pageId={pageId} />
|
|
{locale === 'en' && <HintBar locale={locale} pageId={pageId} />}
|
|
<main id="main" class={mainClass}>
|
|
<slot />
|
|
</main>
|
|
<Footer locale={locale} pageId={pageId} />
|
|
|
|
{/* Global progressive-enhancement modules (deferred, fail-silent). */}
|
|
<script>
|
|
import '@scripts/theme';
|
|
import '@scripts/nav';
|
|
import '@scripts/observer';
|
|
</script>
|
|
</body>
|
|
</html>
|