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.
105 lines
4.1 KiB
CSS
105 lines
4.1 KiB
CSS
@import "tailwindcss";
|
||
|
||
@theme inline {
|
||
--font-sans:
|
||
ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue",
|
||
Arial, "Noto Sans", sans-serif;
|
||
--font-mono:
|
||
ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono",
|
||
monospace;
|
||
}
|
||
|
||
/* ---------------------------------------------------------------- Dark Mode
|
||
|
||
Umgesetzt über `light-dark()` statt über `dark:`-Klassen.
|
||
|
||
Der Grund: Die Oberfläche benutzt an rund 440 Stellen feste Farbklassen wie
|
||
`bg-slate-50` oder `text-slate-900`. Jede davon um eine `dark:`-Variante zu
|
||
ergänzen wäre eine riesige, fehleranfällige Fleißarbeit — und jede künftige
|
||
Zeile müsste daran denken. Stattdessen werden hier die Farbvariablen
|
||
umdefiniert, die Tailwind ohnehin benutzt: `bg-slate-50` wird zu
|
||
`background-color: var(--color-slate-50)`, und was diese Variable enthält,
|
||
entscheidet sich hier.
|
||
|
||
Welche Hälfte von `light-dark()` gilt, steuert `color-scheme` — deshalb
|
||
genügt für den Umschalter ein Attribut am <html>-Element. Ohne Attribut
|
||
folgt die Anwendung dem Betriebssystem, ganz ohne JavaScript.
|
||
|
||
Die dunklen Werte sind keine bloße Spiegelung: Reines Schwarz unter weißer
|
||
Schrift flimmert. Die Flächen liegen deshalb bei 16–26 % Helligkeit, mit
|
||
demselben leicht blauen Ton wie die helle Reihe.
|
||
*/
|
||
|
||
@theme {
|
||
/* Grautöne. Hell: die Reihe von Tailwind. Dunkel: von dunkel nach hell
|
||
gedreht, damit `text-slate-900` weiterhin „die kräftigste Schrift“
|
||
bedeutet und `bg-slate-50` „der ruhigste Grund“. */
|
||
--color-slate-50: light-dark(oklch(98.4% 0.003 247.858), oklch(16.5% 0.018 265));
|
||
--color-slate-100: light-dark(oklch(96.8% 0.007 247.896), oklch(21% 0.02 265));
|
||
--color-slate-200: light-dark(oklch(92.9% 0.013 255.508), oklch(28% 0.022 262));
|
||
--color-slate-300: light-dark(oklch(86.9% 0.022 252.894), oklch(34% 0.026 260));
|
||
--color-slate-400: light-dark(oklch(70.4% 0.04 256.788), oklch(58% 0.032 258));
|
||
--color-slate-500: light-dark(oklch(55.4% 0.046 257.417), oklch(68% 0.03 257));
|
||
--color-slate-600: light-dark(oklch(44.6% 0.043 257.281), oklch(77% 0.025 257));
|
||
--color-slate-700: light-dark(oklch(37.2% 0.044 257.287), oklch(85% 0.02 257));
|
||
--color-slate-800: light-dark(oklch(27.9% 0.041 260.031), oklch(91% 0.014 258));
|
||
--color-slate-900: light-dark(oklch(20.8% 0.042 265.755), oklch(96.5% 0.008 258));
|
||
--color-slate-950: light-dark(oklch(12.9% 0.042 264.695), oklch(99% 0.003 258));
|
||
|
||
/* Flächen. `bg-white` wäre im Dunkeln falsch, `text-white` dagegen richtig
|
||
(es steht auf farbigem Grund) — beide teilen sich aber dieselbe Variable.
|
||
Deshalb tragen Flächen einen eigenen Namen: `bg-surface`. */
|
||
--color-surface: light-dark(#ffffff, oklch(22.5% 0.02 264));
|
||
|
||
/* Akzent auf dunklem Grund. Die Reihe selbst bleibt, wie das
|
||
Erscheinungsbild sie setzt — hier wird nur eine andere Stufe gewählt:
|
||
Fließtext braucht im Dunkeln eine hellere, Flächen eine dunklere. */
|
||
--color-accent-text: light-dark(
|
||
var(--color-indigo-600),
|
||
var(--color-indigo-400)
|
||
);
|
||
--color-accent-soft: light-dark(
|
||
var(--color-indigo-50),
|
||
color-mix(in oklab, var(--color-indigo-500) 22%, oklch(22.5% 0.02 264))
|
||
);
|
||
--color-accent-soft-fg: light-dark(
|
||
var(--color-indigo-700),
|
||
var(--color-indigo-300)
|
||
);
|
||
|
||
/* Immer dunkel, unabhängig vom Thema: die Fläche hinter einem Dialog und
|
||
der Grund einer Videovorschau. */
|
||
--color-scrim: oklch(20.8% 0.042 265.755);
|
||
}
|
||
|
||
:root {
|
||
color-scheme: light dark;
|
||
}
|
||
|
||
:root[data-theme="light"] {
|
||
color-scheme: light;
|
||
}
|
||
|
||
:root[data-theme="dark"] {
|
||
color-scheme: dark;
|
||
}
|
||
|
||
body {
|
||
background-color: var(--color-slate-50);
|
||
color: var(--color-slate-900);
|
||
font-family: var(--font-sans);
|
||
-webkit-font-smoothing: antialiased;
|
||
}
|
||
|
||
/* Sichtbarer, einheitlicher Fokusring für Tastaturbedienung. */
|
||
:focus-visible {
|
||
outline: 2px solid var(--color-accent-text);
|
||
outline-offset: 2px;
|
||
}
|
||
|
||
/* Neutrale Fläche hinter der Vorschau. Der QR-Code bringt seine eigene
|
||
Hintergrundfarbe mit — der leichte Grauton hebt auch weiße Codes ab. */
|
||
.qr-canvas {
|
||
background-color: var(--color-slate-100);
|
||
}
|