From 744bb0bcf3890c77f355c392df12a770e42970ae Mon Sep 17 00:00:00 2001 From: mhoennig Date: Mon, 27 Jul 2026 19:51:11 +0200 Subject: [PATCH] frontend: Strahlenkranz robust machen (Stapelordnung + Pfad-Inversion) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gemeldet von der deployten Seite: „nur die linke Hälfte gelb, kein Rahmen drum herum". Zwei Ursachen gefunden — beide betreffen den Kranz, weil er AUSSERHALB der Knotenbox liegt und damit fremden Malvorgängen ausgeliefert ist: - Fehlende Stapelordnung: Jedes später kommende Geschwister malt seinen undurchsichtigen Hintergrund über den Schein und schneidet ihn einseitig ab — übrig bleibt eine gelbe Kante, die wie ein halber Hintergrund aussieht statt wie ein Rahmen. Die Geschwister-Abstände (gemessen 24–25 px) liegen nur knapp über der Reichweite des Scheins (Blur 16 + Spread 5 = 21 px); bei Zoom, längeren Labels oder engeren Layouts reicht das nicht. Jetzt `z-index:2`. - Pfad-Inversion dimmte die Meldung weg: `.cheap-on .node:not(.cheap)` setzt `opacity:.32; filter:saturate(.4)` und traf damit auch den gelben Kranz (nachgemessen: Deckkraft 0,32 ⇒ praktisch unsichtbar). Gerade bei einer nicht gewählten Alternative ist „das ist jetzt live" aber die interessantere Nachricht. Neue Regel hebt beides für `.fresh` auf. Verifiziert gegen das gebaute dist-Artefakt (dasselbe, das deployt wird): z-index 2 gesetzt; ein nicht-günstiger Knoten mit .fresh hat wieder opacity 1 / filter none bei unverändertem Kranz; im kompakten Modus liegt kein Geschwister mehr über dem Schein. Vitest 46/46. Nicht reproduziert: die exakte Beschreibung (gelb nur hinter der linken Texthälfte) trat bei mir weder im Dev-Server noch im dist-Artefakt auf, in keinem der drei Modi. Die beiden behobenen Defekte erklären sie plausibel, bewiesen ist das nicht. Co-Authored-By: Claude Opus 4.8 --- docs/DECISIONS.md | 17 +++++++++++++++++ frontend/src/style.css | 11 ++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/docs/DECISIONS.md b/docs/DECISIONS.md index 22dae68..c3d9997 100644 --- a/docs/DECISIONS.md +++ b/docs/DECISIONS.md @@ -710,6 +710,23 @@ soll verschwinden, wenn sie ihren Zweck erfüllt hat. Dokument geändert, wird es nicht mehr nachgezogen (D27) — dann gibt es keine saubere Vergleichsbasis, und es wird nichts hervorgehoben. +**Zwei Nachbesserungen nach dem ersten Blick auf die deployte Seite.** Gemeldet +war „nur die linke Hälfte gelb, kein Rahmen drum herum": + +- **`z-index:2` am neuen Knoten.** Der Schein liegt *außerhalb* der Knotenbox. + Ohne eigene Stapelordnung malt jedes **später kommende Geschwister** seinen + undurchsichtigen Hintergrund darüber und schneidet den Kranz einseitig ab — + übrig bleibt eine gelbe Kante, die wie ein halber Hintergrund aussieht statt + wie ein Rahmen. Die Abstände (24–25 px) liegen nur knapp über der Reichweite + des Scheins (Blur 16 + Spread 5 = 21 px); in engeren Layouts, bei Zoom oder + längeren Labels reicht das nicht. +- **Die Pfad-Inversion darf „neu" nicht wegdimmen.** + `.cheap-on .node:not(.cheap)` setzt `opacity:.32; filter:saturate(.4)` — das + trifft auch den gelben Kranz und macht ihn praktisch unsichtbar. Gerade bei + einer *nicht gewählten* Alternative ist „das ist jetzt live" aber die + interessantere Nachricht. Deshalb `.cheap-on .node:not(.cheap).fresh + {opacity:1;filter:none}`. + **Stolperfalle (beim Bauen hineingelaufen):** Die Menge der neuen Knoten muss aus **denselben Knotenobjekten** gebildet werden, die gerade gerendert werden. Zuerst wurde sie beim Laden aus einem eigenen Parse-Durchlauf berechnet — der Zähler diff --git a/frontend/src/style.css b/frontend/src/style.css index e606f5e..76e56dc 100644 --- a/frontend/src/style.css +++ b/frontend/src/style.css @@ -476,7 +476,11 @@ Knoten. Bewusst ein Schein nach AUSSEN statt Farbe im Knoten — die Füllung gehört dem Status (SPEC §4) und bleibt lesbar. Kein Blinken: WCAG 2.2.2/2.3.1 und es zöge dauerhaft den Blick. `#out`-Präfix wegen `ul.or .node`. */ - #out .node.fresh{box-shadow:0 0 0 2px #FACC15,0 0 16px 5px rgba(234,179,8,.65)} + /* `z-index`, weil der Schein AUSSERHALB des Knotens liegt: ohne ihn malt jedes + später kommende Geschwister seinen undurchsichtigen Hintergrund darüber und + schneidet den Kranz einseitig ab (in engen Layouts bleibt nur eine gelbe + Kante stehen — sieht aus wie ein halber Hintergrund statt eines Rahmens). */ + #out .node.fresh{box-shadow:0 0 0 2px #FACC15,0 0 16px 5px rgba(234,179,8,.65);z-index:2} /* Beides zugleich: Cursor-Zeile innen (Tinte), Neuheit außen (gelb). */ #out .node.fresh.current{box-shadow:0 0 0 2px var(--card),0 0 0 4px var(--ink),0 0 18px 6px rgba(234,179,8,.65)} /* Alt gedrückt = Sprung-Geste scharf: Cursor an allen Knoten, Petrol-Ring am @@ -539,6 +543,11 @@ any-of-Alternativen samt Teilbaum) treten zurück (blass, entsättigt); der günstige Pfad hebt sich dadurch von allein ab. */ .cheap-on .node:not(.cheap){opacity:.32;filter:saturate(.4)} + /* „Neu in Produktion" darf die Pfad-Inversion nicht wegdimmen: opacity und + saturate treffen sonst auch den gelben Kranz (0,32 Deckkraft ⇒ praktisch + unsichtbar), und gerade bei einer nicht gewählten Alternative ist „das ist + jetzt live" die interessantere Nachricht (D28). */ + .cheap-on .node:not(.cheap).fresh{opacity:1;filter:none} /* implizit als M angenommene Größe: invertiertes Badge (weiß statt petrol) */ .size.implicit{background:var(--card);color:var(--or);border-color:var(--or)} /* Günstigster-Pfad-Linie: gestrichelt, geschwungen, petrol — fädelt durch