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.
265 lines
6.7 KiB
TypeScript
265 lines
6.7 KiB
TypeScript
import type { SVGProps } from "react";
|
||
|
||
/**
|
||
* Kleines Icon-Set für die QR-Typ-Kacheln und die Oberfläche.
|
||
* Einheitlicher Stil: 24×24, Strichstärke 1.75, runde Enden.
|
||
*/
|
||
const PATHS = {
|
||
globe: (
|
||
<>
|
||
<circle cx="12" cy="12" r="9" />
|
||
<path d="M3 12h18" />
|
||
<path d="M12 3a15 15 0 0 1 0 18a15 15 0 0 1 0-18" />
|
||
</>
|
||
),
|
||
contact: (
|
||
<>
|
||
<rect x="3" y="4" width="18" height="16" rx="2" />
|
||
<circle cx="10" cy="10.5" r="2.25" />
|
||
<path d="M6.5 16.5a4 4 0 0 1 7 0" />
|
||
<path d="M16 9.5h3M16 13h3" />
|
||
</>
|
||
),
|
||
smartphone: (
|
||
<>
|
||
<rect x="6" y="2.5" width="12" height="19" rx="2.5" />
|
||
<path d="M10.5 18.5h3" />
|
||
</>
|
||
),
|
||
list: (
|
||
<>
|
||
<path d="M8 6h12M8 12h12M8 18h12" />
|
||
<circle cx="4" cy="6" r="1.1" />
|
||
<circle cx="4" cy="12" r="1.1" />
|
||
<circle cx="4" cy="18" r="1.1" />
|
||
</>
|
||
),
|
||
share: (
|
||
<>
|
||
<circle cx="17.5" cy="5.5" r="2.5" />
|
||
<circle cx="6.5" cy="12" r="2.5" />
|
||
<circle cx="17.5" cy="18.5" r="2.5" />
|
||
<path d="M8.8 10.8 15.2 6.7M8.8 13.2l6.4 4.1" />
|
||
</>
|
||
),
|
||
chat: (
|
||
<>
|
||
<path d="M21 11.5a8 8 0 0 1-11.9 7L3.5 20l1.6-5.3A8 8 0 1 1 21 11.5Z" />
|
||
<path d="M8.5 10.5h7M8.5 14h4" />
|
||
</>
|
||
),
|
||
document: (
|
||
<>
|
||
<path d="M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8Z" />
|
||
<path d="M14 3v5h5" />
|
||
<path d="M9 13h6M9 16.5h4" />
|
||
</>
|
||
),
|
||
image: (
|
||
<>
|
||
<rect x="3" y="4.5" width="18" height="15" rx="2" />
|
||
<circle cx="8.5" cy="10" r="1.6" />
|
||
<path d="m4 17 4.8-4.4a1.5 1.5 0 0 1 2 0L20 19" />
|
||
</>
|
||
),
|
||
video: (
|
||
<>
|
||
<rect x="3" y="5.5" width="12.5" height="13" rx="2" />
|
||
<path d="m15.5 10.5 5.5-3v9l-5.5-3Z" />
|
||
</>
|
||
),
|
||
mail: (
|
||
<>
|
||
<rect x="3" y="5" width="18" height="14" rx="2" />
|
||
<path d="m3.5 7 8.5 6 8.5-6" />
|
||
</>
|
||
),
|
||
message: (
|
||
<>
|
||
<path d="M20 15a2 2 0 0 1-2 2H8l-4 3.5V6a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2Z" />
|
||
<path d="M8 8.5h8M8 12h5" />
|
||
</>
|
||
),
|
||
pin: (
|
||
<>
|
||
<path d="M12 21s7-6.2 7-11a7 7 0 1 0-14 0c0 4.8 7 11 7 11Z" />
|
||
<circle cx="12" cy="10" r="2.5" />
|
||
</>
|
||
),
|
||
card: (
|
||
<>
|
||
<rect x="2.5" y="5" width="19" height="14" rx="2.5" />
|
||
<path d="M2.5 10h19" />
|
||
<path d="M6.5 14.5h3" />
|
||
</>
|
||
),
|
||
wifi: (
|
||
<>
|
||
<path d="M2.5 9a15 15 0 0 1 19 0" />
|
||
<path d="M5.8 12.6a10 10 0 0 1 12.4 0" />
|
||
<path d="M9.1 16.2a5 5 0 0 1 5.8 0" />
|
||
<circle cx="12" cy="19.5" r="0.9" fill="currentColor" stroke="none" />
|
||
</>
|
||
),
|
||
star: (
|
||
<>
|
||
<path d="m12 3.5 2.6 5.4 5.9.8-4.3 4.2 1 5.9-5.2-2.8-5.2 2.8 1-5.9L3.5 9.7l5.9-.8Z" />
|
||
</>
|
||
),
|
||
qr: (
|
||
<>
|
||
<rect x="3" y="3" width="7" height="7" rx="1.5" />
|
||
<rect x="14" y="3" width="7" height="7" rx="1.5" />
|
||
<rect x="3" y="14" width="7" height="7" rx="1.5" />
|
||
<path d="M14 14h3v3h-3zM18 18h3v3h-3zM14 20.5h1.5" />
|
||
</>
|
||
),
|
||
check: <path d="m4.5 12.5 5 5 10-11" />,
|
||
search: (
|
||
<>
|
||
<circle cx="10.8" cy="10.8" r="6.3" />
|
||
<path d="m15.5 15.5 5 5" />
|
||
</>
|
||
),
|
||
viewList: (
|
||
<>
|
||
<rect x="3" y="4.5" width="18" height="4" rx="1.2" />
|
||
<rect x="3" y="10" width="18" height="4" rx="1.2" />
|
||
<rect x="3" y="15.5" width="18" height="4" rx="1.2" />
|
||
</>
|
||
),
|
||
viewGrid: (
|
||
<>
|
||
<rect x="3.5" y="3.5" width="7" height="7" rx="1.5" />
|
||
<rect x="13.5" y="3.5" width="7" height="7" rx="1.5" />
|
||
<rect x="3.5" y="13.5" width="7" height="7" rx="1.5" />
|
||
<rect x="13.5" y="13.5" width="7" height="7" rx="1.5" />
|
||
</>
|
||
),
|
||
eye: (
|
||
<>
|
||
<path d="M2.5 12s3.6-6.5 9.5-6.5S21.5 12 21.5 12s-3.6 6.5-9.5 6.5S2.5 12 2.5 12Z" />
|
||
<circle cx="12" cy="12" r="3" />
|
||
</>
|
||
),
|
||
eyeOff: (
|
||
<>
|
||
<path d="M10.8 6.1A8.6 8.6 0 0 1 12 5.5c5.9 0 9.5 6.5 9.5 6.5a16 16 0 0 1-3 3.8" />
|
||
<path d="M6.5 6.9A15.7 15.7 0 0 0 2.5 12s3.6 6.5 9.5 6.5a8.8 8.8 0 0 0 4-1" />
|
||
<path d="M9.9 9.9a3 3 0 0 0 4.2 4.2" />
|
||
<path d="m3 3 18 18" />
|
||
</>
|
||
),
|
||
dice: (
|
||
<>
|
||
<rect x="3.5" y="3.5" width="17" height="17" rx="3" />
|
||
<circle cx="8.5" cy="8.5" r="1.1" fill="currentColor" stroke="none" />
|
||
<circle cx="15.5" cy="15.5" r="1.1" fill="currentColor" stroke="none" />
|
||
<circle cx="12" cy="12" r="1.1" fill="currentColor" stroke="none" />
|
||
</>
|
||
),
|
||
copy: (
|
||
<>
|
||
<rect x="9" y="9" width="12" height="12" rx="2" />
|
||
<path d="M15 5.5A2.5 2.5 0 0 0 12.5 3h-7A2.5 2.5 0 0 0 3 5.5v7A2.5 2.5 0 0 0 5.5 15" />
|
||
</>
|
||
),
|
||
download: (
|
||
<>
|
||
<path d="M12 3.5v11" />
|
||
<path d="m7.5 10.5 4.5 4.5 4.5-4.5" />
|
||
<path d="M4 17.5v1.5a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-1.5" />
|
||
</>
|
||
),
|
||
trash: (
|
||
<>
|
||
<path d="M4 6.5h16" />
|
||
<path d="M9 6.5V4.8A1.8 1.8 0 0 1 10.8 3h2.4A1.8 1.8 0 0 1 15 4.8v1.7" />
|
||
<path d="M6.5 6.5 7.4 20a1.5 1.5 0 0 0 1.5 1.4h6.2a1.5 1.5 0 0 0 1.5-1.4l.9-13.5" />
|
||
<path d="M10.5 10.5v7M13.5 10.5v7" />
|
||
</>
|
||
),
|
||
pencil: (
|
||
<>
|
||
<path d="M4 20.5h4l11-11a2.5 2.5 0 0 0-3.5-3.5l-11 11Z" />
|
||
<path d="m14.5 7 2.5 2.5" />
|
||
</>
|
||
),
|
||
arrowLeft: (
|
||
<>
|
||
<path d="M20 12H4" />
|
||
<path d="m10 6-6 6 6 6" />
|
||
</>
|
||
),
|
||
external: (
|
||
<>
|
||
<path d="M14 4h6v6" />
|
||
<path d="M20 4 11 13" />
|
||
<path d="M18 14.5V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4.5" />
|
||
</>
|
||
),
|
||
warning: (
|
||
<>
|
||
<path d="M10.3 4.2 2.6 17.5A2 2 0 0 0 4.3 20.5h15.4a2 2 0 0 0 1.7-3L13.7 4.2a2 2 0 0 0-3.4 0Z" />
|
||
<path d="M12 9.5v4.2" />
|
||
<circle cx="12" cy="17" r="0.9" fill="currentColor" stroke="none" />
|
||
</>
|
||
),
|
||
scan: (
|
||
<>
|
||
<path d="M4 8V6a2 2 0 0 1 2-2h2" />
|
||
<path d="M16 4h2a2 2 0 0 1 2 2v2" />
|
||
<path d="M20 16v2a2 2 0 0 1-2 2h-2" />
|
||
<path d="M8 20H6a2 2 0 0 1-2-2v-2" />
|
||
<path d="M4 12h16" />
|
||
</>
|
||
),
|
||
sun: (
|
||
<>
|
||
<circle cx="12" cy="12" r="4" />
|
||
<path d="M12 2.5v2M12 19.5v2M2.5 12h2M19.5 12h2" />
|
||
<path d="m5.3 5.3 1.4 1.4M17.3 17.3l1.4 1.4M18.7 5.3l-1.4 1.4M6.7 17.3l-1.4 1.4" />
|
||
</>
|
||
),
|
||
moon: (
|
||
<>
|
||
<path d="M20 14.3A8.5 8.5 0 0 1 9.7 4a8.5 8.5 0 1 0 10.3 10.3Z" />
|
||
</>
|
||
),
|
||
monitor: (
|
||
<>
|
||
<rect x="2.5" y="4" width="19" height="13" rx="2" />
|
||
<path d="M9 21h6M12 17v4" />
|
||
</>
|
||
),
|
||
lock: (
|
||
<>
|
||
<rect x="4.5" y="10.5" width="15" height="10.5" rx="2" />
|
||
<path d="M8 10.5V7.5a4 4 0 0 1 8 0v3" />
|
||
</>
|
||
),
|
||
} as const;
|
||
|
||
export type IconName = keyof typeof PATHS;
|
||
|
||
type IconProps = SVGProps<SVGSVGElement> & {
|
||
name: IconName;
|
||
};
|
||
|
||
export function Icon({ name, ...props }: IconProps) {
|
||
return (
|
||
<svg
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
strokeWidth={1.75}
|
||
strokeLinecap="round"
|
||
strokeLinejoin="round"
|
||
aria-hidden="true"
|
||
{...props}
|
||
>
|
||
{PATHS[name]}
|
||
</svg>
|
||
);
|
||
}
|