All checks were successful
Container-Image bauen und veröffentlichen / build-and-push (push) Successful in 1m33s
Rechtsrahmen: § 13 Abs. 3 LBGG SH i. V. m. §§ 3 und 4 BITV 2.0. Eine Erklaerung "nicht barrierefrei" ersetzt die Herstellungspflicht nicht, sie dokumentiert den Stand nur voruebergehend. Farben, alle Werte gegen die WCAG-Formel nachgerechnet: - Sekundaertext 4.40:1 auf muted-Flaechen -> 4.55:1 - Rahmen von Eingabefeldern 1.24:1 -> 3:1 (WCAG 1.4.11); dekorative Trenner bleiben unveraendert - Badge-Farben scheiterten auf der eigenen 10-Prozent-Flaeche (brand 4.20, success 4.18, destructive 3.88) -> jeweils ueber 4.5 - Warnfarbe im Admin 3.11:1 -> amber-700 mit 4.90:1 Fokus war faktisch unsichtbar: Der Ring wurde durchgaengig mit halber Deckkraft gezeichnet (ring-ring/50), effektiv rund 1.5:1. Jetzt volle Deckkraft in Markenfarbe, 12 Dateien betroffen. Weiter: Sprungmarke zum Inhalt auf allen Seiten, Suchfelder beschriftet, Druck-Schaltflaechen hatten als reine Icon-Buttons gar keinen zugaenglichen Namen, Liste als echte Liste ausgezeichnet, doppelter Link je Zeile entfernt, Trefferzahl als Live-Region. Responsiv: Aussenabstaende und Kopfzeilen ab 320 Pixel, Listenzeilen brechen um. Neu nach § 4 BITV 2.0: Seiten fuer Leichte Sprache und Gebaerdensprache, im Admin pflegbar. Leer gelassen und damit noch nicht online - die Inhalte kommen vom Amt. In der Erklaerung als bekannte Luecke benannt. Geprueft mit axe-core (WCAG 2.1 A und AA) ueber alle oeffentlichen Seiten und den Adminbereich, hell und dunkel, dazu Tastaturbedienung, 200 Prozent Vergroesserung und 320 Pixel Breite: keine Verstoesse, kein waagerechter Ueberlauf. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
69 lines
2.6 KiB
TypeScript
69 lines
2.6 KiB
TypeScript
import type { Metadata } from "next"
|
||
import localFont from "next/font/local"
|
||
|
||
import { AppConfigProvider } from "@/components/providers/app-config-provider"
|
||
import { AuthProvider } from "@/components/providers/session-provider"
|
||
import { appName } from "@/lib/app-config"
|
||
import { SkipLink } from "@/components/skip-link"
|
||
import { Toaster } from "@/components/ui/sonner"
|
||
import "./globals.css"
|
||
|
||
// Lokal ausgeliefert aus /font/Inter – keine Anfrage an Google Fonts.
|
||
const inter = localFont({
|
||
src: [
|
||
{ path: "../font/Inter/Inter_18pt-Regular.ttf", weight: "400", style: "normal" },
|
||
{ path: "../font/Inter/Inter_18pt-Italic.ttf", weight: "400", style: "italic" },
|
||
{ path: "../font/Inter/Inter_18pt-Medium.ttf", weight: "500", style: "normal" },
|
||
{ path: "../font/Inter/Inter_18pt-MediumItalic.ttf", weight: "500", style: "italic" },
|
||
{ path: "../font/Inter/Inter_18pt-SemiBold.ttf", weight: "600", style: "normal" },
|
||
{ path: "../font/Inter/Inter_18pt-SemiBoldItalic.ttf", weight: "600", style: "italic" },
|
||
{ path: "../font/Inter/Inter_18pt-Bold.ttf", weight: "700", style: "normal" },
|
||
{ path: "../font/Inter/Inter_18pt-BoldItalic.ttf", weight: "700", style: "italic" },
|
||
],
|
||
variable: "--font-inter",
|
||
display: "swap",
|
||
fallback: ["system-ui", "Segoe UI", "Arial", "sans-serif"],
|
||
})
|
||
|
||
// APP_COMPANY_NAME wird erst zur Laufzeit gelesen. Ohne dynamisches Rendern
|
||
// würde beim Bauen des Images der Vorgabename festgeschrieben.
|
||
export const dynamic = "force-dynamic"
|
||
|
||
export async function generateMetadata(): Promise<Metadata> {
|
||
const name = appName()
|
||
|
||
return {
|
||
title: {
|
||
default: name,
|
||
template: `%s · ${name}`,
|
||
},
|
||
description: "Datenschutzerklärungen nach DSGVO – transparent und zentral verwaltet",
|
||
}
|
||
}
|
||
|
||
export default function RootLayout({
|
||
children,
|
||
}: {
|
||
children: React.ReactNode
|
||
}) {
|
||
return (
|
||
<html lang="de" className={inter.variable} suppressHydrationWarning>
|
||
<head>
|
||
<script
|
||
// Verhindert ein Aufblitzen des hellen Themes vor der Hydration.
|
||
dangerouslySetInnerHTML={{
|
||
__html: `(function(){try{var t=localStorage.getItem('theme')||'system';var d=t==='dark'||(t==='system'&&window.matchMedia('(prefers-color-scheme: dark)').matches);document.documentElement.classList.add(d?'dark':'light');document.documentElement.style.colorScheme=d?'dark':'light';}catch(e){}})();`,
|
||
}}
|
||
/>
|
||
</head>
|
||
<body className={inter.className}>
|
||
<SkipLink />
|
||
<AppConfigProvider appName={appName()}>
|
||
<AuthProvider>{children}</AuthProvider>
|
||
</AppConfigProvider>
|
||
<Toaster />
|
||
</body>
|
||
</html>
|
||
)
|
||
}
|