Die visuelle Ebene war bisher nie geprüft. Zwei Entscheidungen lagen dem
zugrunde: die Apps folgen der Bildsprache des Portals, und es gibt eine
Nachtansicht über prefers-color-scheme — Nachsuchen finden in der
Dämmerung und nachts statt, ein weißes Vollbild blendet dann.
Design-Tokens (App.css ist jetzt die einzige Farbquelle):
- Palette, Georgia-Serif für die Überschriftenebene und die kantigen
2px-Ecken vom Portal übernommen; Sans bleibt für funktionale UI-Texte,
wie es das Portal ebenfalls handhabt.
- Aktionsfarben haben jetzt eine zugehörige --color-on-*-Textfarbe. Ohne
das wäre in der Nachtansicht weißer Text auf hellgrünem Grund gelandet.
- 307 fest verdrahtete Farbwerte pro App auf Tokens umgestellt,
eigenschafts-abhängig (color/background/border getrennt). Das Schlüssel-
wort "white" war dabei der gefährlichste Fall: als Textfarbe auf einer
Vollton-Fläche musste es zu --color-on-* werden, als Fläche zu
--color-surface.
- theme-color war dreifach verschieden (#2d6a2d gehörte zu keiner der
beiden Paletten); Portal und alle Apps nutzen jetzt einen Wert.
Behobene Kontrastfehler (alle nachgerechnet, nicht geschätzt):
- .btn-success 3,13:1 und .btn-primary 4,33:1 lagen unter AA — der
Hauptaktionsbutton der gesamten Anwendung.
- Feldrahmen 2,27:1 und Kartenrahmen 1,60:1 (Soll 3,0 für Bedienelemente).
- .badge-restore 3,46:1 und .badge-password 2,49:1 im Audit-Log.
Geprüft: 36 Paare in hell UND dunkel, 0 Verstöße.
Weitere visuelle Befunde:
- .type-badge war in PublicUserList.css und UserCard.css doppelt
definiert. CSS ist global, die Admin-Variante gewann — die öffentliche
Liste zeigte die falsche Badge-Optik. Fünf solcher Kollisionen zwischen
öffentlicher Liste und Admin-Karte sind jetzt auf ihren Container
eingegrenzt.
- Formularfelder hatten keine Hintergrundfarbe und nahmen den Browser-
Standard: in der Nachtansicht leuchtete jedes Eingabefeld weiß. Fiel
erst im Screenshot auf.
- Die Kartenkacheln sind immer taghell und waren nachts die mit Abstand
hellste Fläche. Sie werden jetzt abgedunkelt, Marker bleiben hell.
- Die Badge-Definitionen im Audit-Log standen doppelt in derselben Datei
(8 Regeln, identische Werte).
Mobiltauglichkeit:
- .user-phone war ein 14 px hoher Textlink — der tel:-Link ist der Zweck
der öffentlichen Liste. Jetzt ein Ziel mit 44 px Mindesthöhe.
- Kopfzeile: Logo verkleinert, .app-title umbricht statt nowrap,
Navigationsschaltflächen auf 44 px, Breakpoint für ≤480px ergänzt,
auf schmalen Geräten nicht mehr klebend.
- 100vh -> 100dvh (Adressleiste erzeugte sonst Überlauf).
- :focus-visible für Buttons; prefers-reduced-motion respektiert.
- Geprüft mit Screenshots bei 360 px und 768 px, hell und dunkel:
kein horizontaler Überlauf.
Aufgeräumt:
- logo-fallingbostel.png und logo-ljn.png entfernt: 898 KB pro App, von
keiner Zeile Code referenziert, 60 % der Buildgröße und fast doppelt so
groß wie der gesamte Anwendungscode. Build 1,5 MB -> 632 KB.
- Tote Stile (.login-prompt, .btn-success, .panel, .user-gps u. a.),
doppelte body-Regel in index.css, Variablentippfehler --color-bg-muted.
- Die Offline-Seite der Apps war lang="en" und ungestaltet — genau die
Seite, die im Funkloch erscheint. Jetzt gestaltet, deutsch, mit
Nachtansicht, für Portal und Apps identisch.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Build läuft über Vite ("build": "vite build"), der Code las
Umgebungsvariablen aber über process.env – das gibt es im Browser-Bundle
nicht. Damit war der Build-Arg REACT_APP_API_URL wirkungslos und der
Zugriff selbst lief in "process is not defined".
- constants.js nutzt import.meta.env und leitet den Deployment-Basispfad
aus import.meta.env.BASE_URL ab (Fallback: Laufzeit-Erkennung).
REACT_APP_* heißt überall VITE_* (Dockerfile, Compose, .env.example,
Doku).
- /verwaltung und /passwort-zuruecksetzen wurden gegen
window.location.pathname ohne Basispfad verglichen und trafen unter
/nachsuche/ nie zu – die Passwort-Reset-Seite war produktiv nicht
erreichbar. Neue Helfer withBase()/normalizePath().
- Der Service Worker wurde als '/sw.js' registriert und war damit der des
Portals mit Scope '/'. Registrierung läuft jetzt über BASE_URL, inkl.
passendem Scope.
- index.html verlinkte '/manifest.json' absolut, also das Portal-Manifest:
die App hätte sich als Portal installiert. Jetzt %BASE_URL%.
- public/index.html (CRA-Rest) kollidierte im Vite-Build mit der
index.html im Projektwurzelverzeichnis und wurde entfernt.
- manifest.json verwies auf ein favicon.ico, das es nicht gibt.
- package.json: Scripts auf Vite umgestellt. react-scripts,
@testing-library/* und web-vitals entfernt – nichts davon wird
importiert, und react-scripts stand im Widerspruch zum Build.
- Die ungenutzten Konstanten USER_TYPES/USER_TYPE_LABELS/RULES sind
entfallen (die echten Werte kommen aus der Datenbank).
- Header bekam eine Prop onXLogin, die er gar nicht entgegennimmt und die
den Logout-Handler durchreichte.
Verifiziert: alle drei Frontends bauen, im Bundle kein process.env mehr,
alle Pfade korrekt auf den jeweiligen Basispfad.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Dockerfile: icon-192.png und icon-512.png in Container aufgenommen
- manifest.json: favicon.ico entfernt (wurde als text/html geliefert)
- manifest.json: Cache-Busting ?v=2 für alle Icons
- index.html: Logo-Bild auf ?v=2 aktualisiert
- extra8002.conf: manifest.json Content-Type auf application/manifest+json gesetzt
- Alle Sub-App manifest.json: scope von '/' auf jeweiligen Pfad korrigiert
(/nachsuche/, /drohnenfuehrer/, /stoeberhunde/)
- icon-192.png und icon-512.png: aus logo-fallingbostel.png generiert