From b828f38ca57975e62a50cca60a8761128482d559 Mon Sep 17 00:00:00 2001 From: Florian Wilde Date: Sun, 16 Aug 2026 09:09:10 +0200 Subject: [PATCH] Dark Mode: folgt dem System, mit Umschalter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drei Zustaende in der Kopfzeile — System, Hell, Dunkel. Drei statt zwei, weil ein reiner Ein/Aus-Schalter nach dem ersten Klick fuer immer eine feste Wahl bedeutet; wer "System" waehlt, bekommt abends dunkel und morgens hell. Umgesetzt ueber `light-dark()` im CSS, nicht ueber `dark:`-Klassen. Die Oberflaeche benutzt an rund 440 Stellen feste Farbklassen; jede davon zu verdoppeln waere eine riesige Fleissarbeit, und jede kuenftige Zeile muesste daran denken. Stattdessen entscheiden die Farbvariablen selbst, welchen Wert sie tragen — gesteuert ueber `color-scheme`. Deshalb genuegt fuer den Umschalter ein Attribut am html-Element, und die Systemwahl funktioniert sogar ohne JavaScript. Das Skript im Kopf setzt nur eine ausdrueckliche Wahl, damit die helle Fassung nicht kurz aufblitzt. Zwei Faelle liessen sich nicht ueber Variablen loesen: `bg-white` muss im Dunkeln eine dunkle Flaeche sein, `text-white` dagegen weiss bleiben — beide teilen sich aber dieselbe Variable. Fuer Flaechen gibt es deshalb jetzt `bg-surface`. Ebenso brauchen Akzent-Links im Dunkeln eine hellere Stufe als Akzent-Knoepfe: dafuer `text-accent-text` und `bg-accent-soft`. Bewusst unveraendert bleiben die QR-Codes selbst — deren Farben legt die Gestaltung fest. Die dunklen Werte sind keine Spiegelung: Reines Schwarz unter weisser Schrift flimmert. Alle Textkontraste sind gemessen und liegen ueber 4,5:1, der niedrigste bei 5,9:1. --- README.md | 2 + docs/funktionen.md | 33 ++++- docs/roadmap.md | 6 + src/app/(app)/konto/page.tsx | 4 +- src/app/(app)/layout.tsx | 12 +- .../(app)/meine-codes/[id]/feedback/page.tsx | 2 +- src/app/(app)/page.tsx | 4 +- src/app/(app)/verwaltung/page.tsx | 8 +- src/app/globals.css | 73 ++++++++++- src/app/layout.tsx | 7 + src/components/branding-admin.tsx | 4 +- src/components/codes-list.tsx | 18 +-- src/components/design-panel.tsx | 12 +- src/components/icons.tsx | 18 +++ src/components/landing/feedback-landing.tsx | 4 +- src/components/landing/image-landing.tsx | 2 +- src/components/landing/links-landing.tsx | 2 +- src/components/landing/pdf-landing.tsx | 4 +- src/components/landing/social-landing.tsx | 2 +- src/components/landing/video-landing.tsx | 2 +- src/components/landing/video-player.tsx | 4 +- src/components/modal.tsx | 2 +- src/components/qr-forms/image-form.tsx | 8 +- src/components/qr-forms/links-form.tsx | 6 +- src/components/qr-forms/pdf-form.tsx | 2 +- src/components/qr-forms/social-form.tsx | 2 +- src/components/qr-forms/website-form.tsx | 2 +- src/components/qr-generator.tsx | 14 +- src/components/qr-preview.tsx | 2 +- src/components/theme-toggle.tsx | 124 ++++++++++++++++++ src/components/type-settings.tsx | 4 +- src/components/ui.tsx | 10 +- 32 files changed, 332 insertions(+), 67 deletions(-) create mode 100644 src/components/theme-toggle.tsx diff --git a/README.md b/README.md index 56de034..149d5c1 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,8 @@ entscheidet die Verwaltung — nicht gebrauchte lassen sich ausblenden. - **Typen abschaltbar:** Was ihr nicht braucht, erscheint gar nicht erst. - **Eigenes Erscheinungsbild:** Name, Logo, Favicon und Akzentfarbe im Admin-Bereich einstellbar. +- **Hell und dunkel:** folgt dem Betriebssystem, mit Umschalter für eine + eigene Wahl. - **Zwei Container, kein Reverse Proxy nötig.** Migrationen laufen beim Start von selbst; alles Nötige steht in einer `.env`. diff --git a/docs/funktionen.md b/docs/funktionen.md index f0f9af9..043810a 100644 --- a/docs/funktionen.md +++ b/docs/funktionen.md @@ -368,7 +368,38 @@ der Stufe 600 steht weiße Schrift. Käme die Helligkeit aus der Eingabe, würde ein helles Gelb die Kopfzeile unlesbar machen — so bleibt jeder Farbton benutzbar. -**Auf den Landingpages** gelten Farbe und Favicon, aber weder Logo noch +### Hell und dunkel + +Die Anwendung folgt von sich aus der Einstellung des Betriebssystems. In der +Kopfzeile steht ein Umschalter mit drei Zuständen: + +| | | +| --- | --- | +| **System** | folgt dem Betriebssystem — abends dunkel, morgens hell | +| **Hell** | immer hell | +| **Dunkel** | immer dunkel | + +Drei statt zwei, weil ein reiner Ein/Aus-Schalter nach dem ersten Klick für +immer eine feste Wahl bedeutet. Die Wahl merkt sich der Browser; ein zweiter +Tab zieht automatisch nach. + +**Wie es umgesetzt ist.** Nicht über `dark:`-Klassen — die hätten an rund 440 +Stellen ergänzt werden müssen, und jede neue Zeile müsste daran denken. +Stattdessen entscheidet `light-dark()` im CSS, welchen Wert die Farbvariablen +tragen; gesteuert wird das über `color-scheme`. Deshalb genügt für den +Umschalter ein Attribut am ``-Element — und die Systemwahl funktioniert +sogar **ohne JavaScript**. + +Die dunklen Werte sind keine Spiegelung der hellen: Reines Schwarz unter +weißer Schrift flimmert. Die Flächen liegen bei 20–26 % Helligkeit im selben +leicht blauen Ton wie die helle Reihe. Alle Textkontraste liegen über der +Anforderung von 4,5:1 — gemessen, nicht geschätzt. + +Unverändert bleiben die QR-Codes selbst. Deren Farben legt die Gestaltung +fest, und ein Code, der hell gedruckt wird, soll in der Vorschau nicht anders +aussehen. + +**Auf den Landingpages** gelten Farbe, Favicon und das Farbthema, aber weder Logo noch Navigation. Wer einen Aushang scannt, soll die Seite des Inhalts sehen, nicht die einer Verwaltungssoftware. Ein Logo je Gruppe auf den Landingpages steht als eigener Punkt auf der [Roadmap](roadmap.md). diff --git a/docs/roadmap.md b/docs/roadmap.md index b7515ce..8fbb738 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -68,6 +68,12 @@ Name, Logo, Favicon und Akzentfarbe im Admin-Bereich einstellbar. Die Farbe wirkt über die Farbvariablen, die Tailwind ohnehin benutzt — deshalb ohne Umbau der rund 60 eingefärbten Stellen. +### Dark Mode (16.08.2026) + +Folgt dem Betriebssystem, mit Umschalter für Hell, Dunkel und System. +Umgesetzt über `light-dark()` statt über `dark:`-Klassen — deshalb ohne +Änderung an den rund 440 Farbklassen und ohne JavaScript für die Systemwahl. + ### Dokumentation (15.08.2026) Bedienungsanleitung mit Bildern, Authentik-Anleitung, Funktionsübersicht. diff --git a/src/app/(app)/konto/page.tsx b/src/app/(app)/konto/page.tsx index 9f5241f..9337312 100644 --- a/src/app/(app)/konto/page.tsx +++ b/src/app/(app)/konto/page.tsx @@ -37,11 +37,11 @@ export default async function AccountPage() { Die Anmeldung ist ausgeschaltet.{" "} Es gibt deshalb kein Konto und niemanden zum Abmelden — wer die Anwendung erreicht, darf alles. Zum Einschalten{" "} - + AUTH_ENABLED=true {" "} in der .env setzen und{" "} - + docker compose up -d {" "} ausführen. Beim nächsten Aufruf legst du den ersten Administrator diff --git a/src/app/(app)/layout.tsx b/src/app/(app)/layout.tsx index 876b91a..91f362c 100644 --- a/src/app/(app)/layout.tsx +++ b/src/app/(app)/layout.tsx @@ -2,6 +2,7 @@ import Link from "next/link"; import { LogoutButton } from "@/components/auth-forms"; import { Icon } from "@/components/icons"; +import { ThemeToggle } from "@/components/theme-toggle"; import { actorLabel, getActor, isAuthEnabled } from "@/lib/auth/actor"; import { can } from "@/lib/auth/policy"; import { getBranding } from "@/lib/branding"; @@ -26,7 +27,7 @@ export default async function AppLayout({ children }: LayoutProps<"/">) { return (
-
+
) {