Nichts verschwindet mehr ohne eine Nachfrage, die sagt was passiert

Filipe: "Es darf vor allem keine Stellen geben, an denen ein Benutzer
etwas falsch machen kann, nur weil die Seite es nicht verstaendlich
genug erklaert."

Gemessen: 30 Stellen in 16 Dateien benutzten confirm() oder prompt().
Das Haus hatte die richtige Bauweise laengst -- einen <dialog>, in
aufgaben.html sogar ausfuehrlich begruendet -- aber sie stand IN EINER
SEITE. Wer anderswo etwas loeschen liess, hatte sie nicht.

confirm('Wirklich loeschen?') stellt die falsche Frage: Es fragt, ob
man sicher ist, und nennt nicht, WAS passiert, was BLEIBT und ob es
ZURUECK geht. Jetzt beantwortet jeder der 41 Dialoge alle drei.

Neu: workspace/assets/js/nachfrage.js -- window.frageNach() mit
Pflichtgrund, Zahlenfeld, einzeiliger Eingabe und Abtippsicherung.
Drei Ausgaenge: <dialog> / confirm()-Notnagel fuer Safari vor 15.4 /
Abbruch (Esc, Klick daneben, "Doch nicht" -- immer false).

DREIMAL DERSELBE FALLSTRICK, dreimal nachgemessen statt vermutet:
  .dialog stand in aufgaben.css und leistung.css -> auf dateien.html
    waere der Dialog ein weisser Systemkasten gewesen. 14 Regeln
    klammergenau nach module.css verschoben (Klammern gezaehlt, nicht
    per Muster geschnitten -- heute frueh hat ein nicht-gieriges
    Muster schon einmal CSS zerrissen).
  Das Formular trug .neu neu--blank -- und .neu gibt seine Abstaende
    nur in aufgaben.css. Gemessen: padding 0px, und die Felder
    verloren ihre height:44px. Jetzt steht alles unter
    .nachfrage__form in module.css; der Dialog borgt nichts mehr.
  Die erste Fassung der Pruefung zaehlte nachfrage.js SELBST als
    Nutzer -- damit war jede Seite trivialerweise "Nutzer" und die
    Pruefung gruen ohne Inhalt. Jetzt ausdruecklich ausgenommen.

ZWEI FUNDE NEBENBEI:
  hilfeAufraeumen() wird im Betrieb NIE aufgerufen. Der Kommentar
    behauptete "wird beim Start aufgerufen (siehe index.js)" -- das
    war nie wahr; einziger Aufrufer ist die eigene Pruefung. Folge:
    geschlossene vertrauliche Faelle bleiben unbegrenzt stehen. NICHT
    eingeschaltet (das loescht echte Daten und ist Filipes
    Entscheidung), sondern der Kommentar richtiggestellt.
  Einen Hilfe-Fall zu schliessen ist endgueltig -- es gibt keine
    Route, die ihn wieder oeffnet. Vorher stand darueber nur die
    Frage nach einem Schlusswort. Jetzt sagt der Dialog es.

pruef-struktur hat meine eigene Pruefung von heute Nachmittag
erwischt: Sie bildete ihr Datum aus UTC. Beim Beheben erst
heuteLokal(datum) genommen -- die Funktion nimmt gar kein Argument
und haette still "heute" statt "+3 Tage" geliefert. Jetzt tagLokal(3),
nachgerechnet: Abstand 3 Tage.

Am Bildschirm angesehen (Rechner 1280, Handy 390): passt rein, Esc
ergibt false, Fokus liegt auf dem harmlosen Knopf, Knoepfe 44px auf
Touch. Der Platzhalter im Abtippfeld zeigte den erwarteten Namen --
das sah aus wie ein schon ausgefuelltes Feld, entfernt.

Neu: server/pruef-nachfrage.mjs -- 17/0, mit sechs Gegenproben und
beiden Richtungen (wer fragt, laedt die Datei; wer nie fragt, laedt
sie nicht -- sonst truege die Anmeldewand 4,8 KB fuer nichts).

pruef-meldungen 8/0, pruef-css-klassen gruen, pruef-struktur gruen,
pruef-leistung gruen.
This commit is contained in:
2026-09-19 19:57:43 +02:00
parent 26937dda16
commit c516aad4ed
65 changed files with 5099 additions and 4111 deletions
-129
View File
@@ -269,132 +269,10 @@
/* ---------- Bearbeiten-Dialog ------------------------------------------ */
/* =====================================================================
DIALOGE (07.09.2026 überarbeitet)
Filipe zu zwei davon: "lass alles in dieser kachel viel spezieller und
geiler aussehen" und "dass soll auch viel besser aussehen".
Er hatte recht, und der Grund war nicht Geschmack: Ein Dialog war
bisher ein grauer Kasten (`background: #0b1018`, ein Rand, fertig) --
während die Seite dahinter aus beleuchteten Platten mit Schienen,
Körnung und Glanz besteht. Beim Öffnen fiel man aus dem einen
Material ins andere.
Jetzt trägt er dieselbe Sprache wie alles übrige:
- Leuchtschiene links, im Hausakzent
- Kopfzeile abgesetzt mit einer feinen Fuge
- Körnung und ein fester Glanz von links oben
- Materialstärke: Licht oben, Schatten unten
Und er ist deutlich TIEFER als vorher (#0b1018 war ein helles Grau
gegen das, was jetzt hinter ihm liegt) -- ein Dialog muss sich vom
Untergrund abheben, sonst schwimmt er darin.
Der Hintergrund dahinter wird STÄRKER abgedunkelt und weicher
gezeichnet: Ein Dialog ist der einzige Ort, an dem ein Weichzeichner
richtig ist -- er soll das Dahinter ausdrücklich zurücktreten
lassen. Genau dafür ist er gemacht, und genau deshalb war er unter
den Kacheln falsch.
===================================================================== */
.dialog {
position: relative;
width: min(580px, calc(100vw - 32px));
padding: 0;
border: 1px solid rgba(150, 186, 220, .16);
/* DIE FASE STATT RUNDER ECKEN (08.09.2026).
Filipe: "alles was sie sehen soll dan auch nach der neuen struktur
aufgebaut sein" -- und ein Dialog ist das, was man am genauesten
ansieht, weil er alles andere verdeckt.
Er kann NICHT in die Modulliste von module.css: Die belegt
`::before` und `::after` fuer die Eckwinkel, und beide sind hier
schon vergeben -- an die Leuchtschiene links und den Lichtsaum.
Beides gegen zwei Winkel zu tauschen waere ein Rueckschritt.
Deshalb hier nur der Zuschnitt, von Hand, mit derselben Groesse
`--fase` wie ueberall. Wer die Fase spaeter aendert, aendert sie
hier mit. */
clip-path: polygon(var(--fase, 18px) 0, 100% 0, 100% 100%, 0 100%, 0 var(--fase, 18px));
border-radius: 0;
background:
linear-gradient(90deg,
color-mix(in srgb, var(--akzent) 16%, transparent) 0%,
color-mix(in srgb, var(--akzent) 5%, transparent) 14%,
transparent 40%),
linear-gradient(163deg, #141c29 0%, #0a0e17 58%, #070a11 100%);
color: var(--text);
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, .10),
inset 0 -1px 0 rgba(0, 0, 0, .55),
inset 2px 0 0 -1px color-mix(in srgb, var(--akzent) 34%, transparent),
0 30px 70px -24px rgba(0, 0, 0, .92),
0 4px 12px -6px rgba(0, 0, 0, .7);
overflow: hidden;
}
/* Die Leuchtschiene -- dieselbe wie an jeder Kachel. */
.dialog::before {
content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 4px;
border-radius: 4px 0 0 4px;
background: linear-gradient(180deg,
color-mix(in srgb, var(--akzent) 55%, #fff) 0%,
var(--akzent) 26%,
color-mix(in srgb, var(--akzent) 48%, transparent) 100%);
box-shadow:
0 0 9px 0 color-mix(in srgb, var(--akzent) 55%, transparent),
3px 0 20px -3px color-mix(in srgb, var(--akzent) 42%, transparent);
z-index: 2;
}
/* Glanz und Körnung. `pointer-events: none` ist Pflicht: Ohne das läge
eine unsichtbare Fläche über dem ganzen Formular, und kein einziges
Feld wäre anklickbar. */
.dialog::after {
content: ""; position: absolute; inset: 0; pointer-events: none;
border-radius: inherit;
background:
linear-gradient(122deg,
rgba(255, 255, 255, .075) 0%,
rgba(255, 255, 255, .020) 15%,
transparent 34%);
/* Ohne Körnung -- siehe die Begründung in automation.css: Auf einer
Ebene mit der Spiegelung erbt sie deren Deckkraft und wird zum
grauen Schleier. */
opacity: .55;
z-index: 1;
}
.dialog > * { position: relative; z-index: 2; }
.dialog::backdrop {
background: rgba(2, 4, 8, .78);
backdrop-filter: blur(6px) saturate(80%);
}
.neu--blank { margin: 0; border: 0; background: none; padding: 22px 24px 20px; }
/* Der Kopf steht auf einer eigenen Stufe: Überschrift, darunter der
erklärende Satz, darunter eine feine Fuge. Ohne diese Trennung
verschwimmt die Ansage mit dem ersten Feld -- und genau die Ansage
ist das, was den Dialog erklärt. */
.dialog__titel {
margin: 0 0 4px;
font-size: 1.24rem; font-weight: 700; letter-spacing: -.015em;
color: #f2f7fd;
}
.dialog__unter {
margin: 0 0 16px; padding-bottom: 16px;
border-bottom: 1px solid rgba(150, 186, 220, .12);
font-size: .87rem; line-height: 1.55;
color: var(--text-leise);
}
/* Hat ein Dialog keine Erklärung, muss die Fuge trotzdem da sein --
sonst hängt die Überschrift am ersten Feld. */
.dialog__titel:last-of-type,
.dialog__unter:empty { border-bottom: 0; }
.dialog__unter:empty {
margin: 0 0 16px; padding-bottom: 16px;
border-bottom: 1px solid rgba(150, 186, 220, .12);
}
.dialog__knoepfe { display: flex; gap: 10px; align-items: center; margin-top: 16px; flex-wrap: wrap; }
.dialog__knoepfe #b-loeschen { margin-left: auto; }
.abmelden--warnung { color: var(--warn); border-color: rgba(255, 154, 162, .35); }
@@ -858,13 +736,6 @@
}
.abbruch-karte .schritt { font-size: .8rem; padding: 7px 13px; }
/* ---------- Der Dialog --------------------------------------------------- */
.dialog__unter {
margin: -4px 0 14px;
font-size: .85rem; color: var(--text-still);
max-width: 60ch; line-height: 1.5;
}
#abbruch-grund {
width: 100%;
-8
View File
@@ -225,9 +225,6 @@
color: var(--text); border-color: var(--rand-hell);
}
/* ---------- Der Import --------------------------------------------------- */
.dialog--breit { max-width: 720px; }
.import-vorschau {
margin: 4px 0 14px; padding: 16px;
@@ -355,7 +352,6 @@
.anleitung li::marker { color: var(--akzent); font-weight: 700; }
.anleitung strong { color: var(--text-leise); }
.dialog__unter--still { font-size: .8rem; color: var(--text-still); }
/* ---- EINGEKLAPPTE HILFE (11.09.2026) ------------------------------
Filipe zum Backstage-Fenster: "die sieht so scheisse aus."
@@ -426,10 +422,6 @@
}
.feld-datei { flex: 1 1 220px; min-width: 0; font-size: .76rem; }
/* Ein Fenster, das laenger ist als der Bildschirm, versteckt seine
eigenen Knoepfe. Gerollt wird INNEN, damit "Uebernehmen" immer
erreichbar bleibt. */
.dialog--breit .neu { max-height: min(86vh, 900px); overflow-y: auto; }
/* =====================================================================
ZEITRÄUME AUS BACKSTAGE — dritte Fassung (16.09.2026)
+256
View File
@@ -559,3 +559,259 @@
der Rundgang sechsmal gemeldet hat. */
.filterreihe__knopf, .k-chip { min-width: 44px; }
}
/* =====================================================================
DER DIALOG — hierher verschoben am 19.09.2026
Er stand in `aufgaben.css` und `leistung.css`. Das war richtig,
solange nur diese zwei Seiten einen Dialog hatten. Seit es
`nachfrage.js` gibt, kann JEDE Seite nachfragen, bevor etwas weg
ist -- und auf einer Seite, die `aufgaben.css` nicht laedt, waere
der Dialog ungestaltet gewesen: weisser Kasten, Systemschrift,
mitten auf einer dunklen Seite.
`module.css` laedt auf jeder Seite (vorletzte Datei, direkt vor
`haus.css`). Was mehr als eine Seite benutzt, gehoert hierher.
In `aufgaben.css` bleibt genau eine Regel: `.dialog__knoepfe
#b-loeschen` -- die haengt an einer ID, die es nur dort gibt.
===================================================================== */
/* =====================================================================
DIALOGE (07.09.2026 überarbeitet)
Filipe zu zwei davon: "lass alles in dieser kachel viel spezieller und
geiler aussehen" und "dass soll auch viel besser aussehen".
Er hatte recht, und der Grund war nicht Geschmack: Ein Dialog war
bisher ein grauer Kasten (`background: #0b1018`, ein Rand, fertig) --
während die Seite dahinter aus beleuchteten Platten mit Schienen,
Körnung und Glanz besteht. Beim Öffnen fiel man aus dem einen
Material ins andere.
Jetzt trägt er dieselbe Sprache wie alles übrige:
- Leuchtschiene links, im Hausakzent
- Kopfzeile abgesetzt mit einer feinen Fuge
- Körnung und ein fester Glanz von links oben
- Materialstärke: Licht oben, Schatten unten
Und er ist deutlich TIEFER als vorher (#0b1018 war ein helles Grau
gegen das, was jetzt hinter ihm liegt) -- ein Dialog muss sich vom
Untergrund abheben, sonst schwimmt er darin.
Der Hintergrund dahinter wird STÄRKER abgedunkelt und weicher
gezeichnet: Ein Dialog ist der einzige Ort, an dem ein Weichzeichner
richtig ist -- er soll das Dahinter ausdrücklich zurücktreten
lassen. Genau dafür ist er gemacht, und genau deshalb war er unter
den Kacheln falsch.
===================================================================== */
.dialog {
position: relative;
width: min(580px, calc(100vw - 32px));
padding: 0;
border: 1px solid rgba(150, 186, 220, .16);
/* DIE FASE STATT RUNDER ECKEN (08.09.2026).
Filipe: "alles was sie sehen soll dan auch nach der neuen struktur
aufgebaut sein" -- und ein Dialog ist das, was man am genauesten
ansieht, weil er alles andere verdeckt.
Er kann NICHT in die Modulliste von module.css: Die belegt
`::before` und `::after` fuer die Eckwinkel, und beide sind hier
schon vergeben -- an die Leuchtschiene links und den Lichtsaum.
Beides gegen zwei Winkel zu tauschen waere ein Rueckschritt.
Deshalb hier nur der Zuschnitt, von Hand, mit derselben Groesse
`--fase` wie ueberall. Wer die Fase spaeter aendert, aendert sie
hier mit. */
clip-path: polygon(var(--fase, 18px) 0, 100% 0, 100% 100%, 0 100%, 0 var(--fase, 18px));
border-radius: 0;
background:
linear-gradient(90deg,
color-mix(in srgb, var(--akzent) 16%, transparent) 0%,
color-mix(in srgb, var(--akzent) 5%, transparent) 14%,
transparent 40%),
linear-gradient(163deg, #141c29 0%, #0a0e17 58%, #070a11 100%);
color: var(--text);
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, .10),
inset 0 -1px 0 rgba(0, 0, 0, .55),
inset 2px 0 0 -1px color-mix(in srgb, var(--akzent) 34%, transparent),
0 30px 70px -24px rgba(0, 0, 0, .92),
0 4px 12px -6px rgba(0, 0, 0, .7);
overflow: hidden;
}
/* Die Leuchtschiene -- dieselbe wie an jeder Kachel. */
.dialog::before {
content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 4px;
border-radius: 4px 0 0 4px;
background: linear-gradient(180deg,
color-mix(in srgb, var(--akzent) 55%, #fff) 0%,
var(--akzent) 26%,
color-mix(in srgb, var(--akzent) 48%, transparent) 100%);
box-shadow:
0 0 9px 0 color-mix(in srgb, var(--akzent) 55%, transparent),
3px 0 20px -3px color-mix(in srgb, var(--akzent) 42%, transparent);
z-index: 2;
}
/* Glanz und Körnung. `pointer-events: none` ist Pflicht: Ohne das läge
eine unsichtbare Fläche über dem ganzen Formular, und kein einziges
Feld wäre anklickbar. */
.dialog::after {
content: ""; position: absolute; inset: 0; pointer-events: none;
border-radius: inherit;
background:
linear-gradient(122deg,
rgba(255, 255, 255, .075) 0%,
rgba(255, 255, 255, .020) 15%,
transparent 34%);
/* Ohne Körnung -- siehe die Begründung in automation.css: Auf einer
Ebene mit der Spiegelung erbt sie deren Deckkraft und wird zum
grauen Schleier. */
opacity: .55;
z-index: 1;
}
.dialog > * { position: relative; z-index: 2; }
.dialog::backdrop {
background: rgba(2, 4, 8, .78);
backdrop-filter: blur(6px) saturate(80%);
}
/* Der Kopf steht auf einer eigenen Stufe: Überschrift, darunter der
erklärende Satz, darunter eine feine Fuge. Ohne diese Trennung
verschwimmt die Ansage mit dem ersten Feld -- und genau die Ansage
ist das, was den Dialog erklärt. */
.dialog__titel {
margin: 0 0 4px;
font-size: 1.24rem; font-weight: 700; letter-spacing: -.015em;
color: #f2f7fd;
}
.dialog__unter {
margin: 0 0 16px; padding-bottom: 16px;
border-bottom: 1px solid rgba(150, 186, 220, .12);
font-size: .87rem; line-height: 1.55;
color: var(--text-leise);
}
/* Hat ein Dialog keine Erklärung, muss die Fuge trotzdem da sein --
sonst hängt die Überschrift am ersten Feld. */
.dialog__titel:last-of-type,
.dialog__unter:empty { border-bottom: 0; }
.dialog__unter:empty {
margin: 0 0 16px; padding-bottom: 16px;
border-bottom: 1px solid rgba(150, 186, 220, .12);
}
.dialog__knoepfe { display: flex; gap: 10px; align-items: center; margin-top: 16px; flex-wrap: wrap; }
/* ---------- Der Dialog --------------------------------------------------- */
.dialog__unter {
margin: -4px 0 14px;
font-size: .85rem; color: var(--text-still);
max-width: 60ch; line-height: 1.5;
}
/* ---------- Der Import --------------------------------------------------- */
.dialog--breit { max-width: 720px; }
.dialog__unter--still { font-size: .8rem; color: var(--text-still); }
/* Ein Fenster, das laenger ist als der Bildschirm, versteckt seine
eigenen Knoepfe. Gerollt wird INNEN, damit "Uebernehmen" immer
erreichbar bleibt. */
.dialog--breit .neu { max-height: min(86vh, 900px); overflow-y: auto; }
/* ---- Die zwei Zeilen, die confirm() nicht kann ---------------------
`nachfrage.js` baut den Dialog; diese beiden Teile gibt es nur dort.
Die Endgueltig-Zeile ist der wichtigste Satz im ganzen Dialog --
sie beantwortet die einzige Frage, die vor einem Loeschen zaehlt.
Deshalb bekommt sie eine eigene Farbe UND ein Zeichen davor: Farbe
allein traegt keine Aussage (Rot-Gruen-Schwaeche, Kontrastmodus),
und im Kontrastmodus wird `color` ohnehin ersetzt. Das Zeichen
bleibt in jedem Fall. */
.nachfrage__endgueltig {
margin: 0 0 4px;
padding: 8px 10px;
font-size: .86rem;
line-height: 1.45;
color: #ffd9d2;
background: rgba(190, 74, 60, .13);
border-left: 3px solid rgba(214, 96, 80, .62);
}
.nachfrage__endgueltig::before { content: "! "; font-weight: 800; }
/* Der Fehler IM Dialog (z. B. fehlender Grund). Er darf nicht
aussehen wie der Fehlerstreifen der Seite dahinter -- der ist
verdeckt, dieser hier gehoert zum Formular. Leer nimmt er keinen
Platz weg, sonst rutschten die Knoepfe beim ersten Fehler. */
.nachfrage__fehler { margin: 0; font-size: .86rem; color: #ffb4a8; }
.nachfrage__fehler:empty { display: none; }
/* Am Handy klebt der Dialog sonst am oberen Rand und die Knoepfe
liegen unter der Tastatur. Er waechst von unten, wie ein Blatt,
das hochgezogen wird -- das ist dort die gewohnte Richtung. */
@media (max-width: 560px) {
.nachfrage { margin: auto auto 0; width: 100vw; max-width: 100vw; }
}
@media (forced-colors: active) {
.nachfrage__endgueltig { border-left: 3px solid CanvasText; background: Canvas; }
}
/* Der kleine Hinweis unter einem Feld im Nachfrage-Dialog ("Du kannst
bis zu 14 Tage geben"). Er steht UNTER dem Feld, nicht daneben:
daneben rutscht er am Handy weg, und dort liest ihn niemand. */
.nachfrage__hinweis { margin: 4px 0 0; font-size: .8rem; color: var(--text-still); }
.nachfrage__hinweis:empty { display: none; }
/* ---- Der Dialog steht auf eigenen Füßen ---------------------------
NACHGEMESSEN AM 19.09.2026, und es war genau derselbe Fehler wie
bei `.dialog` selbst: Das Formular trug erst die Klassen
`neu neu--blank` — und `.neu` gibt seine Abstände NUR in
`aufgaben.css`. Gemessen auf einer Seite ohne diese Datei:
`padding: 0px`. Der Text klebte am Rand, und die Eingabefelder
verloren ihre `height: 44px` — also ausgerechnet die Größe, die
ein Daumen trifft.
Am Bildschirm sah es trotzdem "irgendwie richtig" aus, weil die
Schrift und die Farben aus `gate.css` kamen. Solche Abhängigkeiten
sieht man nicht, man misst sie.
Deshalb steht hier alles, was der Dialog braucht — er borgt nichts
mehr aus einer Seitendatei. `.knopf`, `.abmelden` und `.feld-schild`
bleiben: die stehen in `gate.css` bzw. `start.css`, und die lädt
jede Seite. */
.nachfrage__form { margin: 0; padding: 20px; display: grid; gap: 10px; }
.nachfrage__zeile { display: grid; gap: 5px; }
.nachfrage__form input,
.nachfrage__form textarea {
width: 100%;
box-sizing: border-box;
font: inherit;
color: var(--text);
background: var(--tinte);
border: 1px solid var(--rand);
border-radius: var(--radius, 10px);
padding: 8px 10px;
}
/* 44 Pixel, weil ein Daumen weniger nicht sicher trifft. Dieselbe Zahl
wie überall im Haus -- sie steht hier, damit sie nicht davon abhängt,
welche Seitendatei zufällig geladen ist. */
.nachfrage__form input { height: 44px; }
.nachfrage__form textarea { min-height: 62px; resize: vertical; }
.nachfrage__form input:focus,
.nachfrage__form textarea:focus {
outline: none;
box-shadow: inset 0 2px 5px -3px rgba(0, 0, 0, .75),
0 0 0 3px rgba(63, 189, 245, .16);
}
/* Die Knöpfe unten bekommen Luft nach oben -- sonst sitzt "Löschen"
direkt unter dem Satz, der davor warnt. */
.nachfrage .dialog__knoepfe { margin-top: 6px; }