feat(t5): Darstellung hell/dunkel/Sonne ohne Flackern (T5a A2)

Blockierendes Skript im <head> setzt Darstellung, Kontrast und
Bewegung vor dem ersten Zeichnen (gleiche Logik wie resolveAppearance,
für alle Eingaben getestet), theme-color je Satz, Wahl je Gerät in
localStorage mit Rückfall bei gesperrtem Speicher; Abgleich über Tabs,
Systemeinstellungen und Seitenwechsel an einer Stelle. Profil-Karte
„Darstellung“, Sonnenmodus im Benutzermenü; Dunkel bleibt bis A4 hinter
NEXT_PUBLIC_APPEARANCE_DARK. next-themes entfernt. ARCHITECTURE §9:
'unsafe-inline' der Frontend-CSP als getragenes Risiko mit Begründung.

Refs #26

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Nick.RosenloecherandClaude Opus 5.5 committed 2026-10-16 16:35:28 +02:00
1 parent 947a46f435
commit cf53cf35ac
22 files changed
+980 -97

No files matched your search

+16 -1
View File
@@ -3,8 +3,10 @@
import { ReactNode, useEffect, useRef, useState } from "react";
import Link from "next/link";
import { usePathname, useRouter } from "next/navigation";
import { Hexagon } from "lucide-react";
import { Hexagon, Sun } from "lucide-react";
import { toast } from "sonner";
import { useAuth } from "@/lib/auth/auth-context";
import { useAppearance } from "@/lib/appearance/appearance";
import { finishHandOvers, purgeStaleDraftPhotos } from "@/lib/photos/store";
import { LogoutConfirmDialog } from "@/components/auth/logout-confirm-dialog";
import { NotificationBell } from "@/components/notification-bell";
@@ -12,6 +14,7 @@ import { OfflineBand } from "@/components/offline/offline-band";
import { UserAvatar } from "@/components/user-avatar";
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
@@ -32,6 +35,7 @@ export default function AppLayout({ children }: { children: ReactNode }) {
const pathname = usePathname();
const { user, isLoading, sessionMode } = useAuth();
const [isLogoutOpen, setIsLogoutOpen] = useState(false);
const { sunActive, setContrast } = useAppearance();
// Focus lands here when the offline queue sheet closes and nothing else is left.
const pageContentRef = useRef<HTMLDivElement>(null);
@@ -113,6 +117,17 @@ export default function AppLayout({ children }: { children: ReactNode }) {
</DropdownMenuLabel>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuCheckboxItem
checked={sunActive}
onCheckedChange={(checked) => {
setContrast(checked ? "sun" : "normal");
toast.success(checked ? "Sonnenmodus an" : "Sonnenmodus aus");
}}
className="min-h-11"
>
<Sun aria-hidden="true" />
Sonnenmodus
</DropdownMenuCheckboxItem>
<DropdownMenuItem render={<Link href="/profile" />}>
Profil
</DropdownMenuItem>