Base.astro

 1---
 2import Header from '../components/Header.astro';
 3import Footer from '../components/Footer.astro';
 4import '../styles/footer.css';
 5
 6interface Props {
 7  title: string;
 8  description?: string;
 9  activeNav?: 'home' | 'designing' | 'docs' | 'slop' | 'live';
10  canonicalPath?: string;
11  bodyClass?: string;
12  noIndex?: boolean;
13  mainId?: string;
14  mainClass?: string;
15  hideHeader?: boolean;
16  hideFooter?: boolean;
17  ogTitle?: string;
18  ogDescription?: string;
19  ogImage?: string;
20  twitterSite?: string;
21  twitterCreator?: string;
22}
23
24const {
25  title,
26  description = 'Design fluency for AI-assisted frontend development.',
27  activeNav,
28  canonicalPath,
29  bodyClass,
30  noIndex = false,
31  mainId = 'main',
32  mainClass,
33  hideHeader = false,
34  hideFooter = false,
35  ogTitle,
36  ogDescription,
37  ogImage,
38  twitterSite,
39  twitterCreator,
40} = Astro.props;
41
42const canonical = canonicalPath
43  ? `https://impeccable.style${canonicalPath}`
44  : undefined;
45---
46
47<!DOCTYPE html>
48<html lang="en">
49<head>
50  <meta charset="UTF-8">
51  <meta name="viewport" content="width=device-width, initial-scale=1.0">
52  <title>{title}</title>
53  <meta name="description" content={description}>
54  <meta name="theme-color" content="#fafafa">
55  {noIndex && <meta name="robots" content="noindex">}
56  {canonical && <link rel="canonical" href={canonical}>}
57
58  {ogTitle && (
59    <>
60      <meta property="og:type" content="website">
61      <meta property="og:url" content={canonical || 'https://impeccable.style'}>
62      <meta property="og:title" content={ogTitle}>
63      <meta property="og:description" content={ogDescription || description}>
64      {ogImage && <meta property="og:image" content={ogImage}>}
65    </>
66  )}
67
68  {twitterSite && (
69    <>
70      <meta name="twitter:card" content="summary_large_image">
71      <meta name="twitter:site" content={twitterSite}>
72      {twitterCreator && <meta name="twitter:creator" content={twitterCreator}>}
73      <meta name="twitter:title" content={ogTitle || title}>
74      <meta name="twitter:description" content={ogDescription || description}>
75      {ogImage && <meta name="twitter:image" content={ogImage}>}
76    </>
77  )}
78
79  <link rel="icon" type="image/svg+xml" href="/favicon.svg">
80  <link rel="preconnect" href="https://fonts.googleapis.com">
81  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
82  <link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;0,600;0,700;1,300;1,400&family=Instrument+Sans:wght@400;500;600;700&family=Inter:wght@400;500;600&family=Space+Grotesk:wght@400;500;600&display=swap" rel="stylesheet">
83  <slot name="head" />
84</head>
85<body class={bodyClass}>
86  <a href={`#${mainId}`} class="skip-link">Skip to content</a>
87  <slot name="before-header" />
88  {!hideHeader && <Header activeNav={activeNav} />}
89  <slot name="after-header" />
90  <main id={mainId} class={mainClass}>
91    <slot />
92  </main>
93  {!hideFooter && <Footer />}
94  <slot name="scripts" />
95</body>
96</html>