| 1 | import type { Metadata } from "next"; |
| 2 | import "./globals.css"; |
| 3 | import { Inter } from "next/font/google"; |
| 4 | import { ThemeProvider } from "@/components/theme-provider"; |
| 5 | import Footer from "@/components/footer"; |
| 6 | import Header from "@/components/header"; |
| 7 | import { StructuredData } from "@/components/structured-data"; |
| 8 | import siteMetadata from "@/content/siteMetadata"; |
| 9 | import { absoluteUrl } from "./seo"; |
| 10 | |
| 11 | const inter = Inter({ |
| 12 | subsets: ["latin"], |
| 13 | variable: "--font-inter", |
| 14 | display: "swap", |
| 15 | }); |
| 16 | |
| 17 | export const metadata: Metadata = { |
| 18 | metadataBase: new URL(siteMetadata.siteUrl), |
| 19 | applicationName: siteMetadata.title, |
| 20 | title: { |
| 21 | default: siteMetadata.title, |
| 22 | template: `%s | ${siteMetadata.title}`, |
| 23 | }, |
| 24 | description: siteMetadata.description, |
| 25 | keywords: siteMetadata.keywords, |
| 26 | authors: [{ name: siteMetadata.author }], |
| 27 | creator: siteMetadata.author, |
| 28 | publisher: siteMetadata.companyName, |
| 29 | category: "Developer tools", |
| 30 | alternates: { |
| 31 | canonical: "/", |
| 32 | }, |
| 33 | openGraph: { |
| 34 | title: siteMetadata.title, |
| 35 | description: siteMetadata.description, |
| 36 | url: siteMetadata.siteUrl, |
| 37 | siteName: siteMetadata.title, |
| 38 | locale: "en_US", |
| 39 | type: "website", |
| 40 | images: [ |
| 41 | { |
| 42 | url: siteMetadata.socialBanner || "/twitter-card.png", |
| 43 | alt: siteMetadata.tagline, |
| 44 | }, |
| 45 | ], |
| 46 | }, |
| 47 | twitter: { |
| 48 | card: "summary_large_image", |
| 49 | title: siteMetadata.title, |
| 50 | description: siteMetadata.description, |
| 51 | images: [siteMetadata.socialBanner || "/twitter-card.png"], |
| 52 | }, |
| 53 | robots: { |
| 54 | index: true, |
| 55 | follow: true, |
| 56 | googleBot: { |
| 57 | index: true, |
| 58 | follow: true, |
| 59 | "max-image-preview": "large", |
| 60 | "max-snippet": -1, |
| 61 | "max-video-preview": -1, |
| 62 | }, |
| 63 | }, |
| 64 | icons: { |
| 65 | icon: [{ url: "/favicon.ico" }], |
| 66 | shortcut: [{ url: "/favicon.ico" }], |
| 67 | }, |
| 68 | }; |
| 69 | |
| 70 | const globalStructuredData = { |
| 71 | "@context": "https://schema.org", |
| 72 | "@graph": [ |
| 73 | { |
| 74 | "@type": "Organization", |
| 75 | "@id": absoluteUrl("/#organization"), |
| 76 | name: siteMetadata.organizationName, |
| 77 | legalName: siteMetadata.companyName, |
| 78 | url: siteMetadata.siteUrl, |
| 79 | logo: absoluteUrl(siteMetadata.siteLogo), |
| 80 | email: siteMetadata.email, |
| 81 | sameAs: [ |
| 82 | siteMetadata.github, |
| 83 | siteMetadata.facebook, |
| 84 | siteMetadata.instagram, |
| 85 | siteMetadata.youtube, |
| 86 | ].filter(Boolean), |
| 87 | address: { |
| 88 | "@type": "PostalAddress", |
| 89 | addressLocality: "Stockholm", |
| 90 | addressCountry: "SE", |
| 91 | }, |
| 92 | }, |
| 93 | { |
| 94 | "@type": "WebSite", |
| 95 | "@id": absoluteUrl("/#website"), |
| 96 | url: siteMetadata.siteUrl, |
| 97 | name: siteMetadata.title, |
| 98 | description: siteMetadata.description, |
| 99 | inLanguage: siteMetadata.language, |
| 100 | publisher: { |
| 101 | "@id": absoluteUrl("/#organization"), |
| 102 | }, |
| 103 | }, |
| 104 | ], |
| 105 | }; |
| 106 | |
| 107 | interface RootLayoutProps { |
| 108 | children: React.ReactNode; |
| 109 | } |
| 110 | |
| 111 | export default function RootLayout({ children }: RootLayoutProps) { |
| 112 | return ( |
| 113 | <html lang="en" suppressHydrationWarning> |
| 114 | <body |
| 115 | className={`${inter.variable} font-sans antialiased min-h-screen bg-white dark:bg-[#0A0E14] text-slate-900 dark:text-slate-50`} |
| 116 | > |
| 117 | <StructuredData data={globalStructuredData} /> |
| 118 | <ThemeProvider attribute="class" defaultTheme="dark" enableSystem> |
| 119 | <div className="flex min-h-screen flex-col"> |
| 120 | <Header /> |
| 121 | <main className="flex-1">{children}</main> |
| 122 | <Footer /> |
| 123 | </div> |
| 124 | </ThemeProvider> |
| 125 | </body> |
| 126 | </html> |
| 127 | ); |
| 128 | } |