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
+190
View File
@@ -308,6 +308,77 @@
/* Die Glaskacheln bekommen eine Leuchtkante in der Leitfarbe. Das
bindet Inhalt und Buehne zusammen, statt die Kacheln wie
aufgeklebte Zettel wirken zu lassen. */
/* ---------- 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, gemessen im
Browser. 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. */
.vw-tabs {
position: relative;
}
.vw-tabs::after {
content: "";
position: absolute;
left: 0;
top: var(--vw-balken-y, 100%);
width: var(--vw-balken-b, 0px);
height: 2px;
border-radius: 2px;
transform: translateX(var(--vw-balken-x, 0px));
background: linear-gradient(90deg,
rgb(var(--vw-farbe) / 0) 0%,
rgb(var(--vw-farbe) / .95) 22%,
rgb(var(--vw-farbe) / .95) 78%,
rgb(var(--vw-farbe) / 0) 100%);
box-shadow: 0 0 16px -2px rgb(var(--vw-farbe) / .75);
transition:
transform .42s cubic-bezier(.22,.9,.24,1),
width .42s cubic-bezier(.22,.9,.24,1),
top .42s cubic-bezier(.22,.9,.24,1);
pointer-events: none;
/* Erst zeigen, wenn er vermessen ist -- sonst blitzt beim Laden
kurz ein Balken der Breite 0 an der linken Kante auf. */
opacity: var(--vw-balken-da, 0);
}
@media (prefers-reduced-motion: reduce) {
.vw-tabs::after { transition: none; }
}
/* ---------- KACHELN TAUCHEN AUF ----------
Beim Bereichswechsel erscheinen die Kacheln nacheinander statt alle
auf einmal. Der Versatz ist klein; er soll den Blick von oben nach
unten fuehren, nicht auf eine Animation warten lassen.
Nur die ersten acht bekommen einen Versatz. Bei einer langen Liste
wuerde die letzte Kachel sonst spuerbar spaeter kommen -- das
fuehlt sich nicht mehr elegant an, sondern langsam. */
@keyframes vw-kachel-auf {
from { opacity: 0; transform: translateY(14px) scale(.985); }
to { opacity: 1; transform: none; }
}
#vw-bereich .wd-karte {
animation: vw-kachel-auf .5s cubic-bezier(.2,.8,.3,1) both;
}
#vw-bereich .wd-karte:nth-child(1) { animation-delay: .02s; }
#vw-bereich .wd-karte:nth-child(2) { animation-delay: .07s; }
#vw-bereich .wd-karte:nth-child(3) { animation-delay: .12s; }
#vw-bereich .wd-karte:nth-child(4) { animation-delay: .17s; }
#vw-bereich .wd-karte:nth-child(5) { animation-delay: .22s; }
#vw-bereich .wd-karte:nth-child(6) { animation-delay: .27s; }
#vw-bereich .wd-karte:nth-child(7) { animation-delay: .32s; }
#vw-bereich .wd-karte:nth-child(n+8) { animation-delay: .36s; }
@media (prefers-reduced-motion: reduce) {
#vw-bereich .wd-karte { animation: none; }
}
/* ---------- DAS LICHT FOLGT DER MAUS ----------
Das Lumen-Licht war nie weg: --wd-lichtx/--wd-lichty werden weiter
@@ -376,6 +447,30 @@
0 1px 4px rgb(3 8 16 / .9);
}
/* Auch die Reiter reagieren auf den Zeiger. Der Punkt ist klein und
sitzt genau unter dem Finger -- er soll die Flaeche beleben, nicht
vom Beschriftungstext ablenken. */
@media (hover: hover) and (pointer: fine) {
#vw-bereich .vw-filter-knopf {
position: relative;
overflow: hidden;
isolation: isolate;
}
#vw-bereich .vw-filter-knopf::before {
content: "";
position: absolute;
inset: 0;
z-index: -1;
background: radial-gradient(90px circle at var(--wd-lichtx, 50%) var(--wd-lichty, 50%),
rgb(var(--vw-farbe) / .30) 0%,
rgb(var(--vw-farbe) / .10) 45%,
transparent 72%);
opacity: 0;
transition: opacity .25s ease;
}
#vw-bereich .vw-filter-knopf:hover::before { opacity: 1; }
}
/* Suchen und Abmelden sitzen oben rechts -- also genau dort, wo das
Motiv am hellsten ist. Bedienelemente muessen immer lesbar sein,
egal was dahinter liegt. Deshalb bekommen sie hier einen eigenen,
@@ -3450,6 +3545,98 @@
}
signaturSetzen(aktiverTab);
balkenSetzen();
/* ---------------------------------------------------------------------
Der Leuchtbalken unter der Reiterreihe.
Er wird am echten Reiter ausgemessen. Feste Werte waeren hier
zwangslaeufig falsch: Die Reiter sind unterschiedlich breit, sie
verschieben sich beim Sprachwechsel, und auf schmalen Schirmen
brechen sie in eine zweite Zeile um -- deshalb wandert auch die
Hoehe mit, nicht nur die Seite.
--------------------------------------------------------------------- */
function balkenSetzen() {
var leiste = document.querySelector("#vw-bereich .vw-tabs");
if (!leiste) return;
var aktiv = leiste.querySelector(".vw-filter-knopf.vw-an");
if (!aktiv) { leiste.style.setProperty("--vw-balken-da", "0"); return; }
var l = leiste.getBoundingClientRect();
var a = aktiv.getBoundingClientRect();
/* Relativ zur Leiste rechnen, nicht zum Fenster: Sonst wandert der
Balken beim Scrollen mit dem Fenster statt mit dem Reiter. */
leiste.style.setProperty("--vw-balken-x", Math.round(a.left - l.left) + "px");
leiste.style.setProperty("--vw-balken-b", Math.round(a.width) + "px");
leiste.style.setProperty("--vw-balken-y", Math.round(a.bottom - l.top + 4) + "px");
leiste.style.setProperty("--vw-balken-da", "1");
}
/* Neu vermessen, sobald sich an der Leiste etwas aendert.
Ein blosser "resize"-Horcher reicht nicht. Der haeufigste Fall ist
naemlich gar keine Fenstergroessenaenderung, sondern das
Sichtbarwerden: Beim Start ist der Arbeitsbereich noch versteckt,
die Leiste also 0 Punkte breit -- und ein verstecktes Element
loest kein resize aus. Genau das ist beim Bauen passiert: Der
Balken stand auf Breite 0 und blieb unsichtbar.
Ein ResizeObserver deckt beides ab -- Sichtbarwerden, Umbrechen der
Reiter auf schmalen Schirmen und den Sprachwechsel, bei dem die
Beschriftungen unterschiedlich lang werden. */
var balkenWartet = false;
function balkenNeuMessen() {
if (balkenWartet) return;
balkenWartet = true;
requestAnimationFrame(function () { balkenWartet = false; balkenSetzen(); });
}
window.addEventListener("resize", balkenNeuMessen, { passive: true });
/* Nach aussen verfuegbar machen.
Der Balken haengt sonst allein am Klick-Listener. Wechselt der
Bereich auf einem anderen Weg -- etwa direkt nach dem Anmelden,
wenn der Arbeitsbereich zum ersten Mal sichtbar wird -- wird nie
nachgemessen, und er bleibt auf Breite 0 stehen. */
window.vwBalkenSetzen = balkenSetzen;
(function balkenBeobachten() {
var leiste = document.querySelector("#vw-bereich .vw-tabs");
if (!leiste || typeof ResizeObserver === "undefined") return;
try {
new ResizeObserver(balkenNeuMessen).observe(leiste);
} catch (e) { /* aeltere Browser: dann eben nur bei resize */ }
})();
/* ---------------------------------------------------------------------
Das Licht folgt dem Zeiger auch auf den Reitern.
Die Verfolgung im Grundsystem greift ausdruecklich nur auf Karten.
Hier wird sie fuer die Reiter ergaenzt -- gedrosselt ueber
requestAnimationFrame, aus demselben Grund wie dort.
--------------------------------------------------------------------- */
(function reiterLicht() {
try {
if (!window.matchMedia("(hover: hover) and (pointer: fine)").matches) return;
} catch (e) { return; }
var offen = false, letzter = null, zx = 0, zy = 0;
document.addEventListener("pointermove", function (e) {
var knopf = e.target && e.target.closest
? e.target.closest("#vw-bereich .vw-filter-knopf") : null;
if (!knopf) return;
letzter = knopf; zx = e.clientX; zy = e.clientY;
if (offen) return;
offen = true;
requestAnimationFrame(function () {
offen = false;
if (!letzter) return;
var r = letzter.getBoundingClientRect();
letzter.style.setProperty("--wd-lichtx", (zx - r.left) + "px");
letzter.style.setProperty("--wd-lichty", (zy - r.top) + "px");
});
}, { passive: true });
})();
/* ---------------------------------------------------------------------
Die Buehne bewegt sich leicht gegen den Mauszeiger.
@@ -3498,6 +3685,9 @@
document.querySelectorAll("[data-tab]").forEach(function (x) {
x.classList.toggle("vw-an", x.dataset.tab === aktiverTab);
});
/* Erst NACH dem Umsetzen der Klasse messen -- vorher zeigt der
Balken noch auf den alten Reiter. */
balkenSetzen();
/* Auf dem Handy sind die Reiter ein Wischstreifen. Wer ueber eine
Kachel nach "Postfach" springt, landet sonst in einer Ansicht,
deren Reiter ausserhalb des Streifens liegt -- man sieht dann