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

@@ -2,6 +2,8 @@ import { useMutation } from "@tanstack/react-query";
import { act, render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { afterEach, describe, expect, it, vi } from "vitest";
import { readFileSync } from "node:fs";
import path from "node:path";
import { Providers } from "./providers";
vi.mock("@/lib/auth/auth-context", () => ({
@@ -37,4 +39,26 @@ describe("Providers", () => {
await userEvent.click(screen.getByRole("button", { name: "Speichern" }));
await waitFor(() => expect(mutationFn).toHaveBeenCalledTimes(1));
});
it("does not apply the motion level itself any more (the inline script does)", () => {
document.documentElement.removeAttribute("data-motion");
window.localStorage.setItem("apicore.motion", "off");
render(
<Providers>
<p>x</p>
</Providers>,
);
expect(document.documentElement).not.toHaveAttribute("data-motion");
window.localStorage.clear();
});
});
describe("theme handling", () => {
it("the toaster follows resolvedTheme and next-themes is not imported", () => {
const toaster = readFileSync(path.resolve(__dirname, "../components/ui/sonner.tsx"), "utf8");
expect(toaster).toContain("resolvedTheme");
expect(toaster).not.toContain("next-themes");
const pkg = JSON.parse(readFileSync(path.resolve(__dirname, "../../package.json"), "utf8"));
expect(pkg.dependencies["next-themes"]).toBeUndefined();
});
});