Files
flimmer/src/app/layout.tsx
T
Florian Wilde 18c87e6b2e fix: Schrift mitliefern statt beim Bauen herunterladen
next/font/google laedt Outfit beim Bauen von Google — das macht den Build von
einer Netzverbindung abhaengig und liess ihn im Container haengen. Die
variable Fassung liegt jetzt als Datei im Repository; weder beim Bauen noch
zur Laufzeit geht eine Anfrage nach aussen.

Ausserdem: npm blockiert seit Version 11 Install-Skripte. Fuer prisma und
@prisma/engines sind sie jetzt gezielt erlaubt, damit im Image die
plattformpassende Engine bereitliegt.
2026-09-01 20:56:32 +02:00

95 lines
3.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { Metadata, Viewport } from "next";
import localFont from "next/font/local";
import { BrandDefs } from "@/components/brand/FlimmerLogo";
import { ToastProvider } from "@/components/Toasts";
import { ServiceWorkerRegistration } from "@/components/ServiceWorkerRegistration";
import { env } from "@/lib/env";
import "./globals.css";
/**
* Outfit liegt als Datei im Repository.
*
* next/font/google würde die Schrift beim Bauen herunterladen — das macht den
* Build von einer Netzverbindung abhängig und ließ ihn im Container hängen.
* Für eine selbst gehostete Anwendung ist die mitgelieferte Datei ohnehin das
* Richtige: kein Abruf beim Bauen, keiner zur Laufzeit.
*
* Es ist die variable Fassung (Strichstärke 100–900), aufgeteilt in Latin und
* Latin-Extended — der Browser lädt nur, was die Seite wirklich braucht.
*/
const outfit = localFont({
src: [
{ path: "../../public/fonts/outfit-latin.woff2", weight: "100 900", style: "normal" },
{ path: "../../public/fonts/outfit-latin-ext.woff2", weight: "100 900", style: "normal" },
],
display: "swap",
variable: "--font-outfit",
fallback: ["Inter", "Segoe UI", "system-ui", "sans-serif"],
});
export const metadata: Metadata = {
metadataBase: new URL(env.appUrl),
title: {
default: "Flimmer",
template: "%s · Flimmer",
},
description: "Serien und Filme tracken — selbst gehostet, ohne fremde Konten.",
applicationName: "Flimmer",
manifest: "/manifest.webmanifest",
icons: {
icon: [
{ url: "/favicon.svg", type: "image/svg+xml" },
{ url: "/favicon.ico", sizes: "48x48" },
],
apple: "/icons/apple-touch-icon.png",
},
openGraph: {
title: "Flimmer",
description: "Serien und Filme tracken — selbst gehostet, ohne fremde Konten.",
images: ["/og-image.png"],
type: "website",
},
};
export const viewport: Viewport = {
themeColor: [
{ media: "(prefers-color-scheme: dark)", color: "#0c0a14" },
{ media: "(prefers-color-scheme: light)", color: "#faf7f5" },
],
width: "device-width",
initialScale: 1,
viewportFit: "cover",
};
/**
* Setzt data-theme, bevor der erste Pixel gemalt wird — sonst blitzt beim
* Laden kurz das falsche Farbschema auf.
*/
const THEME_BOOTSTRAP = `(function(){try{
var stored=localStorage.getItem("flimmer-theme");
var mode=stored==="light"||stored==="dark"?stored:
(stored==="system"||!stored)&&window.matchMedia("(prefers-color-scheme: light)").matches?"light":"dark";
document.documentElement.setAttribute("data-theme",mode);
}catch(e){document.documentElement.setAttribute("data-theme","dark");}})();`;
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="de" data-theme="dark" className={outfit.variable} suppressHydrationWarning>
<head>
<script dangerouslySetInnerHTML={{ __html: THEME_BOOTSTRAP }} />
</head>
<body className="antialiased">
<BrandDefs />
<a
href="#inhalt"
className="sr-only focus:not-sr-only focus:fixed focus:left-4 focus:top-4 focus:z-50 fl-btn fl-btn-primary"
>
Zum Inhalt springen
</a>
<ToastProvider>{children}</ToastProvider>
<ServiceWorkerRegistration />
</body>
</html>
);
}