Dark Mode: folgt dem System, mit Umschalter

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.
This commit is contained in:
Florian Wilde
2026-08-16 09:09:10 +02:00
parent 4046b72626
commit b828f38ca5
32 changed files with 332 additions and 67 deletions
+7 -7
View File
@@ -226,7 +226,7 @@ export function QrGenerator({ typeId }: { typeId: QrTypeId }) {
</Link>
<div className="mb-7 flex flex-wrap items-center gap-3">
<span className="flex size-11 items-center justify-center rounded-xl bg-indigo-50 text-indigo-600">
<span className="flex size-11 items-center justify-center rounded-xl bg-accent-soft text-accent-text">
<Icon name={qrType.icon} className="size-6" />
</span>
<div>
@@ -290,8 +290,8 @@ export function QrGenerator({ typeId }: { typeId: QrTypeId }) {
className={cx(
"rounded-xl border p-4 text-left transition-colors disabled:cursor-not-allowed disabled:opacity-50",
!isDynamic
? "border-indigo-600 bg-indigo-50/60"
: "border-slate-300 bg-white hover:bg-slate-50",
? "border-indigo-600 bg-accent-soft/60"
: "border-slate-300 bg-surface hover:bg-slate-50",
)}
>
<span className="flex items-center gap-2 font-medium text-slate-900">
@@ -314,8 +314,8 @@ export function QrGenerator({ typeId }: { typeId: QrTypeId }) {
className={cx(
"rounded-xl border p-4 text-left transition-colors disabled:cursor-not-allowed disabled:opacity-50",
isDynamic
? "border-indigo-600 bg-indigo-50/60"
: "border-slate-300 bg-white hover:bg-slate-50",
? "border-indigo-600 bg-accent-soft/60"
: "border-slate-300 bg-surface hover:bg-slate-50",
)}
>
<span className="flex items-center gap-2 font-medium text-slate-900">
@@ -353,7 +353,7 @@ export function QrGenerator({ typeId }: { typeId: QrTypeId }) {
<span className="font-medium">Code gespeichert.</span> Der
QR-Code zeigt dauerhaft auf diese Kurz-URL:
<span className="mt-2 flex flex-wrap items-center gap-2">
<code className="rounded bg-white/70 px-2 py-1 font-mono text-xs break-all">
<code className="rounded bg-surface/70 px-2 py-1 font-mono text-xs break-all">
{saved.shortUrl}
</code>
<Button
@@ -383,7 +383,7 @@ export function QrGenerator({ typeId }: { typeId: QrTypeId }) {
) : null}
<Link
href="/meine-codes"
className="inline-flex items-center gap-1.5 rounded-lg text-sm font-medium text-indigo-600 hover:text-indigo-700"
className="inline-flex items-center gap-1.5 rounded-lg text-sm font-medium text-accent-text hover:text-accent-soft-fg"
>
Zu „Meine Codes“
<Icon name="external" className="size-3.5" />