Verwaltung: gleitender Leuchtbalken, gestaffelte Kacheln, Reiterlicht

Drei weitere Stufen auf der Buehne.

1. DER GLEITENDE LEUCHTBALKEN

Unter der Reiterreihe liegt ein Balken in der Leitfarbe. Beim Wechsel
springt er nicht, sondern gleitet zum neuen Reiter und faerbt sich dabei
um.

Position und Breite kommen aus dem ECHTEN Reiter, im Browser gemessen.
Feste Werte waeren hier zwangslaeufig falsch: Die Reiter sind
unterschiedlich breit ("Kunden" gegen "Zahlungen"), sie verschieben sich
beim Sprachwechsel, und auf schmalen Schirmen brechen sie um -- deshalb
wandert auch die Hoehe mit, nicht nur die Seite.

2. DIE KACHELN TAUCHEN GESTAFFELT AUF

Beim Bereichswechsel erscheinen sie nacheinander statt alle auf einmal.
Nur die ersten acht bekommen einen Versatz -- bei einer langen Liste
kaeme die letzte Kachel sonst spuerbar spaeter, und das fuehlt sich
nicht mehr elegant an, sondern langsam.

3. DAS LICHT FOLGT AUCH AUF DEN REITERN

Die Verfolgung im Grundsystem greift ausdruecklich nur auf Karten. Fuer
die Reiter ist sie hier ergaenzt, gedrosselt ueber
requestAnimationFrame -- aus demselben Grund wie dort.

ZWEI FEHLER, DIE DER TEST GEFUNDEN HAT:

Der Balken stand auf Breite 0 und blieb unsichtbar. Ein blosser
"resize"-Horcher reicht naemlich nicht: Der haeufigste Fall ist gar
keine Fenstergroessenaenderung, sondern das Sichtbarwerden. Beim Start
ist der Arbeitsbereich versteckt, die Leiste also 0 Punkte breit -- und
ein verstecktes Element loest kein resize aus. Jetzt beobachtet ein
ResizeObserver die Leiste; das deckt Sichtbarwerden, Umbrechen und
Sprachwechsel gleichermassen ab.

Und die Messung hing allein am Klick-Listener. Wechselt der Bereich auf
einem anderen Weg -- etwa direkt nach dem Anmelden, wenn der
Arbeitsbereich zum ersten Mal auftaucht -- wurde nie nachgemessen.
balkenSetzen ist deshalb jetzt nach aussen verfuegbar.

Alles Bewegte bleibt bei prefers-reduced-motion aus: kein Gleiten, kein
Auftauchen, keine Parallaxe, kein Reflex. Die Buehne bleibt sichtbar.

Ein Wort zum Test selbst: Er prueft "der Balken springt" nicht mehr auf
wortwoertlich "0s". Die Testumgebung emuliert reduzierte Bewegung, indem
sie Uebergaenge auf eine Mikrosekunde setzt statt auf null -- gemeldet
wird "1e-06s". Wahrnehmbar ist das identisch; ein Test auf exakt "0s"
haette nur die Emulation gemessen, nicht die Regel.

Geprueft: 244 Pruefungen gruen (Verwaltung 46, Portfolio 15, CRYONOVA 53,
Blickfang 13, System 5, Abmelden 16, Abbruch 42, Angebot 54).

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-08-25 07:02:51 +02:00
co-authored by Claude Opus 5
parent a74d924445
commit fdf3e5349e
7 changed files with 287 additions and 1 deletions
+96
View File
@@ -287,6 +287,78 @@ srv.listen(4404, async () => {
const weite = Math.max(...String(lage2).match(/-?[\d.]+/g).map((n) => Math.abs(+n)));
pruefe("und zwar nur wenige Bildpunkte", weite <= 16, weite.toFixed(1) + "px");
console.log("\n===== 3d. DER LEUCHTBALKEN =====");
/* Der Balken muss GENAU am aktiven Reiter sitzen. Gemessen wird
gegen die echten Masse des Reiters -- feste Werte waeren hier
zwangslaeufig falsch, weil die Reiter unterschiedlich breit sind
und beim Sprachwechsel wandern. */
const balkenPruefen = async (bereich) => {
/* Erst den Bereich sichtbar machen, DANN klicken: Ein Klick auf
einen zugeklappten Arbeitsbereich misst zwangslaeufig 0.
Danach wieder aufmachen -- der Klick loest die Zugangspruefung
aus, die korrekterweise zur Anmeldung zurueckschaltet. */
await bereichAufbauen(seite, bereich);
await seite.click(`[data-tab="${bereich}"]`).catch(() => {});
await seite.waitForTimeout(500);
await bereichAufbauen(seite, bereich);
await seite.evaluate(() => window.vwBalkenSetzen && window.vwBalkenSetzen());
await seite.waitForTimeout(400);
return seite.evaluate(() => {
const l = document.querySelector("#vw-bereich .vw-tabs");
const a = l.querySelector(".vw-filter-knopf.vw-an");
const lr = l.getBoundingClientRect();
const ar = a.getBoundingClientRect();
const st = l.style;
return {
gesetztX: parseInt(st.getPropertyValue("--vw-balken-x"), 10),
gesetztB: parseInt(st.getPropertyValue("--vw-balken-b"), 10),
echtX: Math.round(ar.left - lr.left),
echtB: Math.round(ar.width),
sichtbar: st.getPropertyValue("--vw-balken-da"),
reiter: a.textContent.trim().slice(0, 12),
};
});
};
const b1 = await balkenPruefen("uebersicht");
pruefe("der Balken sitzt am aktiven Reiter",
Math.abs(b1.gesetztX - b1.echtX) <= 2 && Math.abs(b1.gesetztB - b1.echtB) <= 2,
`${b1.reiter}: gesetzt ${b1.gesetztX}/${b1.gesetztB}, echt ${b1.echtX}/${b1.echtB}`);
pruefe("und er ist sichtbar", b1.sichtbar === "1", b1.sichtbar);
/* Der haeufigste Fehlerfall: Beim Start ist der Arbeitsbereich noch
versteckt, die Leiste also 0 Punkte breit. Wer nur auf "resize"
horcht, misst nie nach -- der Balken bleibt auf Breite 0. */
pruefe("er hat eine echte Breite (nicht 0)", b1.gesetztB > 20, b1.gesetztB + "px");
const b2 = await balkenPruefen("zahlungen");
pruefe("er wandert zum neuen Reiter mit",
b2.gesetztX !== b1.gesetztX && Math.abs(b2.gesetztX - b2.echtX) <= 2,
`${b1.gesetztX}px -> ${b2.gesetztX}px`);
console.log("\n===== 3e. TAUCHEN DIE KACHELN GESTAFFELT AUF? =====");
const staffel = await seite.evaluate(() => {
const be = document.querySelector("#vw-bereich");
const probe = document.querySelector("#vw-probe");
probe.innerHTML = "";
for (let i = 0; i < 4; i++) {
const k = document.createElement("div");
k.className = "wd-karte";
k.style.padding = "1rem";
k.textContent = "Kachel " + (i + 1);
probe.appendChild(k);
}
return [...probe.querySelectorAll(".wd-karte")].map((k) => ({
name: getComputedStyle(k).animationName,
versatz: getComputedStyle(k).animationDelay,
}));
});
pruefe("die Kacheln haben eine Auftauch-Animation",
staffel.every((k) => k.name === "vw-kachel-auf"),
staffel.map((k) => k.name).join(", "));
pruefe("und jede startet etwas spaeter als die davor",
new Set(staffel.map((k) => k.versatz)).size === staffel.length,
staffel.map((k) => k.versatz).join(" "));
console.log("\n===== 4. WECHSELT DER RAUM WIRKLICH? =====");
const gesehen = [];
for (const bereich of BEREICHE) {
@@ -344,6 +416,30 @@ srv.listen(4404, async () => {
});
pruefe("keine Parallaxe bei reduzierter Bewegung",
ruhigeLage.translate === "none", ruhigeLage.translate);
const ruhigExtra = await ruhig.evaluate(() => {
const be = document.querySelector("#vw-bereich");
const k = document.createElement("div");
k.className = "wd-karte";
be.appendChild(k);
const l = document.querySelector("#vw-bereich .vw-tabs");
return {
kachel: getComputedStyle(k).animationName,
balken: getComputedStyle(l, "::after").transitionDuration,
};
});
pruefe("keine Auftauch-Animation der Kacheln",
ruhigExtra.kachel === "none", ruhigExtra.kachel);
/* Geprueft wird "praktisch sofort", nicht wortwoertlich "0s".
Die Testumgebung emuliert reduzierte Bewegung, indem sie alle
Uebergaenge auf eine Mikrosekunde setzt statt auf null -- gemeldet
wird dann "1e-06s". Wahrnehmbar ist das identisch; ein Test auf
exakt "0s" wuerde nur die Emulation messen, nicht die Regel. */
const laengste = Math.max(...ruhigExtra.balken.split(",")
.map((t) => parseFloat(t) || 0));
pruefe("und der Balken gleitet nicht, sondern springt",
laengste <= 0.01, ruhigExtra.balken);
pruefe("und kein Lichtreflex",
ruhigeLage.reflex === "none" && ruhigeLage.reflexSichtbar === 0,
ruhigeLage.reflex + ", Deckkraft " + ruhigeLage.reflexSichtbar);