Files
AfiS-Aktensystem/web/app/layout.tsx
T
Harlekin e0b03ea336
Prüfung / Lint, Typen, Tests (push) Canceled after 0s
Prüfung / Images bauen (pdf) (push) Canceled after 0s
Prüfung / Images bauen (web) (push) Canceled after 0s
Prüfung / Images bauen (worker) (push) Canceled after 0s
Images / bauen (pdf, docker/pdf.Dockerfile) (push) Canceled after 0s
Images / bauen (web, docker/web.Dockerfile) (push) Canceled after 0s
Images / bauen (worker, docker/worker.Dockerfile) (push) Canceled after 0s
Gedankenstriche durch Bindestriche ersetzt
375 Vorkommen in 210 Dateien. Betrifft Kommentare, Oberflaechentexte und die
README; an keiner Stelle Bedeutung, nur Zeichensetzung.
2026-09-04 08:54:52 +02:00

86 lines
3.0 KiB
TypeScript

import type { Metadata, Viewport } from 'next'
import { headers } from 'next/headers'
import { de } from '@/lib/i18n/de'
import { THEME_KEY } from '@/components/ThemeToggle'
import './globals.css'
export const metadata: Metadata = {
title: { default: de.app.name, template: `%s · ${de.app.shortName}` },
description: de.app.description,
robots: { index: false, follow: false },
}
export const viewport: Viewport = {
themeColor: '#15181A',
width: 'device-width',
initialScale: 1,
}
/**
* Setzt das gewählte Thema, **bevor** das erste Mal gemalt wird.
*
* Ohne das blitzt beim Laden kurz das falsche Thema auf. Es ist die eine
* Stelle mit `dangerouslySetInnerHTML`, und der Inhalt ist eine Konstante aus
* diesem Modul - kein Nutzertext. Das Verbot aus docs/07 gilt Nutzerinhalten.
*/
const THEME_SCRIPT =
`try{var t=localStorage.getItem(${JSON.stringify(THEME_KEY)});` +
`if(t==='dark'||t==='light')document.documentElement.setAttribute('data-theme',t)}catch(e){}`
export default async function RootLayout({ children }: { children: React.ReactNode }) {
// Die strikte Inhaltsrichtlinie erlaubt nur Skripte mit passendem Nonce,
// siehe middleware.ts.
const nonce = (await headers()).get('x-nonce') ?? undefined
return (
<html lang="de">
<head>
{/*
`suppressHydrationWarning` wegen des Nonce, nicht wegen des Inhalts.
Der Browser **leert das Nonce-Attribut**, sobald er das Element
geparst hat - so schreibt es die CSP-Spezifikation vor, damit kein
Skript den Wert über `getAttribute` auslesen und sich selbst
nachladen kann. React vergleicht beim Hydrieren aber genau dieses
Attribut, liest clientseitig den leeren Wert und meldet einen
Unterschied, den es nicht gibt: unser Markup ist auf beiden Seiten
dasselbe.
Es bleibt bei einem einzigen Element mit einer Konstante als Inhalt,
deshalb ist die Unterdrückung hier eng genug. Der Nonce selbst
entfällt nicht: ohne ihn blockiert die Richtlinie das Skript, und
ohne das Skript blitzt beim Laden das falsche Thema auf.
*/}
<script
nonce={nonce}
suppressHydrationWarning
dangerouslySetInnerHTML={{ __html: THEME_SCRIPT }}
/>
{/* Nur die beiden Schnitte vorladen, die auf jeder Seite vorkommen. */}
<link
rel="preload"
href="/fonts/ibm-plex-sans-latin-400-normal.woff2"
as="font"
type="font/woff2"
crossOrigin="anonymous"
/>
<link
rel="preload"
href="/fonts/ibm-plex-mono-latin-400-normal.woff2"
as="font"
type="font/woff2"
crossOrigin="anonymous"
/>
</head>
<body>
<a
href="#inhalt"
className="sr-only focus:not-sr-only focus:absolute focus:top-2 focus:left-2 focus:z-50 focus:border focus:border-line focus:bg-panel focus:px-3 focus:py-2"
>
{de.nav.skipToContent}
</a>
{children}
</body>
</html>
)
}