Agentur-Events: zwei Karten nebeneinander, Banner in der Mitte
Filipe: "ich will dass die kachel an sich noch viel geiler und krass
aussieht. die soll aber auch garnicht so breit sein. es sollen immer
zwei neben einander stehen können und wenn man auf banner anzeigen
drückt soll das banner schön in der mitte sein."
GEMESSEN VORHER: Karte 1160 px breit, eine je Reihe (#liste stand auf
display: block). --ton: #cace02 an ALLEN drei Zustaenden -- ein seit
drei Tagen vorbeigelaufenes Event leuchtete wie eines, das laeuft. Und
border-left-width: 0px, border-radius: 0px: Die Dringlichkeitskante aus
bereich.css kommt seit dem Baukasten nicht mehr an (module.css setzt
border: 0, gleich stark und spaeter im Ladeweg).
ZWEI NEBENEINANDER -- die Regel SIEBT, statt zu schalten. #liste traegt
das Raster immer; alles, was keine Eventkarte ist, nimmt mit
`grid-column: 1 / -1` die ganze Breite. In den sieben anderen Bereichen
aendert sich damit kein Pixel. Gemessen: Eventkarten 573 px, die
Schulung daneben weiterhin 1160 px; auf 412 px steht wieder eine je
Reihe (379 px, kein seitliches Schieben).
DAS BANNER KOMMT IN DIE MITTE -- und zwar in der BILDSCHAU, die es seit
dem 25.09.2026 gibt und die hier nur nie angeschlossen war. Sie wurde
damals fuer genau diese Beschwerde gebaut ("wenn man bilder aufmacht,
will ich dass die in der mitte sind"). Ihr Stil ist dafuer aus chat.css
in eine eigene bildschau.css gezogen: bereich.html laedt chat.css nicht,
und 5815 Zeilen Chat auf einer Seite ohne Chat waere der falsche Preis.
Gemessen: 1178x662 px, Mitte 640 gegen 640, Escape schliesst, die Seite
springt nicht weg. Der Weg in den Tab bleibt (mittlere Maustaste, Strg).
ZWEI FEHLER, DIE DIE SCHMALERE KARTE ERST SICHTBAR GEMACHT HAT:
1. `justify-content: flex-end` an der Buehne laesst den Inhalt nach
OBEN herauslaufen, wenn der Platz nicht reicht -- und die Karte hat
ein clip-path, also ist er dann weg. Gemessen: Das Etikett
"Agentur-Events" sass auf einer Karte bei y = -28 px. Die alte
Messung fragte nach Farbe, Groesse, Sichtbarkeit und Deckkraft und
bekam auf alles die richtige Antwort -- nach der LAGE hatte sie nie
gefragt. Jetzt `margin-top: auto`, das kann nicht herauslaufen.
2. Die Buehnenhoehe kam aus der BREITE (aspect-ratio 2.6/1, bei
"vorbei" 3.6/1 mit max-height 190px). Bei halber Kartenbreite
bricht der Titel auf mehr Zeilen um: Inhalt 231 px, Kasten 220 px
-- der Zeitbalken lag auf dem Satz darunter. Jetzt eine
Mindesthoehe, der Rest kommt vom Inhalt. Das Banner liegt absolut
darin und fuellt jede Hoehe; die Rechnung brauchte es nie.
AUSSERDEM: Jeder Zustand traegt seinen Ton (laeuft = Agenturgelb,
kommt = kuehl, vorbei = stumpf) und faerbt damit Eckwinkel, Kantenlicht
und Zeitbalken mit. Die Farbkante der Karte liegt im Hintergrund statt
auf einem Pseudoelement (beide gehoeren dem Baukasten), 6 px, weil das
Kantenlicht 1,6 px verdeckt. Der Aufgabenzaehler ist eine Pille statt
grauer Text, gleich hohe Karten mit dem Fuss unten.
pruef-eventkarte: 77 -> 94 Pruefungen, gruen. Zwei Gegenproben gefahren
(Ueberlaufschutz entfernt -> rot mit "ev-buehne__inhalt oben -28px";
die neue Lagepruefung nennt den Schuldigen selbst). Neu darin: nichts
darf aus einer Karte herausragen (132 Teile), der Inhalt muss in jede
Buehne passen, und auf 412 px steht eine Karte je Reihe.
Dazu gruen: chat-anhaenge (168, die Bildschau nach dem Umzug),
chat-optik, chat, css-klassen, haus-trennung, lesbarkeit, eintrag-bild,
tippziele, fingermass, leerzustand.
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -471,6 +471,252 @@ async function alsRolle(rolle, code, breite = 1280) {
|
||||
});
|
||||
melde(" -- gemessen: " + JSON.stringify(masse));
|
||||
|
||||
/* ==== NUR MESSEN (07.10.2026): Breite, Ton und tote Regeln ==== */
|
||||
const lage = await seite.evaluate(() => {
|
||||
const k = [...document.querySelectorAll('.eintrag-karte[data-eventkarte="ja"]')];
|
||||
const erste = k[0], zweite = k[1];
|
||||
const s = getComputedStyle(erste);
|
||||
const liste = document.getElementById("liste");
|
||||
return {
|
||||
kartenBreite: Math.round(erste.getBoundingClientRect().width),
|
||||
listenBreite: Math.round(liste.getBoundingClientRect().width),
|
||||
nebeneinander: zweite
|
||||
? Math.abs(erste.getBoundingClientRect().top - zweite.getBoundingClientRect().top) < 5 : null,
|
||||
ton: s.getPropertyValue("--ton").trim() || "(nicht gesetzt)",
|
||||
randLinks: s.borderLeftWidth, randOben: s.borderTopWidth,
|
||||
radius: s.borderTopLeftRadius,
|
||||
anzeige: getComputedStyle(liste).display,
|
||||
/* Wo haengt die Karte wirklich? Im 4000-Zeilen-Modul stehen drei
|
||||
Formen (Kanalspalten, Abschnitte nach Art, Zeitstrahl) -- die
|
||||
Antwort aus dem Quelltext zu raten waere Raten. */
|
||||
eltern: (() => {
|
||||
const weg = []; let n = erste.parentElement;
|
||||
for (let i = 0; i < 4 && n; i++, n = n.parentElement) {
|
||||
weg.push((n.tagName + "." + (n.className || "")).trim()
|
||||
+ (n.id ? "#" + n.id : "") + ":" + getComputedStyle(n).display);
|
||||
}
|
||||
return weg;
|
||||
})(),
|
||||
geschwister: erste.parentElement
|
||||
? [...erste.parentElement.children].map((c) => c.tagName + "." + (c.className || "").split(" ")[0])
|
||||
: [],
|
||||
reihen: [...liste.children].map((c) => {
|
||||
const r = c.getBoundingClientRect();
|
||||
return {
|
||||
event: c.dataset.eventkarte === "ja",
|
||||
titel: (c.querySelector(".eintrag-titel")?.textContent || "?").slice(0, 18),
|
||||
x: Math.round(r.x), y: Math.round(r.y), b: Math.round(r.width),
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
melde(" -- Lage: " + JSON.stringify(lage));
|
||||
|
||||
/* ===== ZWEI NEBENEINANDER (07.10.2026) ============================
|
||||
|
||||
Filipe: „die soll aber auch garnicht so breit sein. es sollen immer
|
||||
zwei neben einander stehen können."
|
||||
|
||||
Gemessen VOR dem Umbau: 1160 px breit, eine je Reihe. */
|
||||
const events = lage.reihen.filter((r) => r.event);
|
||||
const andere = lage.reihen.filter((r) => !r.event);
|
||||
ok(events.length >= 2 && events.every((r) => r.b < lage.listenBreite * 0.55),
|
||||
`jede Eventkarte ist hoechstens halb so breit wie die Liste `
|
||||
+ `(${events.map((r) => r.b).join(", ")} von ${lage.listenBreite} px)`);
|
||||
/* DASS SIE SCHMAL SIND, HEISST NOCH NICHT, DASS ZWEI NEBENEINANDER
|
||||
STEHEN -- sie koennten auch untereinander in einer halben Spalte
|
||||
haengen. Gemessen wird deshalb ein PAAR: zwei Karten, gleiche
|
||||
Hoehe, verschiedene Seite. */
|
||||
const paar = events.some((a, i) => events.slice(i + 1)
|
||||
.some((b2) => Math.abs(a.y - b2.y) < 5 && Math.abs(a.x - b2.x) > 100));
|
||||
ok(paar, `zwei Eventkarten stehen wirklich nebeneinander `
|
||||
+ `(${events.map((r) => `${r.titel}@${r.x}/${r.y}`).join(" ")})`);
|
||||
/* GEGENPROBE: Was KEIN Event ist, bleibt ueber die ganze Breite --
|
||||
sonst waere „die Liste ist jetzt zweispaltig" ein Umbau aller acht
|
||||
Bereiche und nicht der Eventkarten. */
|
||||
ok(andere.length > 0 && andere.every((r) => r.b > lage.listenBreite * 0.95),
|
||||
`die Nicht-Event-Karte behaelt die volle Breite (${andere.map((r) => r.b).join(", ")} px)`);
|
||||
|
||||
/* ===== DIE KARTE SAGT, OB DAS EVENT LAEUFT ======================== */
|
||||
const toene = await seite.evaluate(() => {
|
||||
const raus = {};
|
||||
for (const k of document.querySelectorAll('.eintrag-karte[data-eventkarte="ja"]')) {
|
||||
raus[k.dataset.event] = getComputedStyle(k).getPropertyValue("--ton").trim();
|
||||
}
|
||||
return raus;
|
||||
});
|
||||
/* ==== NUR MESSEN: Wie sieht eine Karte OHNE Banner aus? ====
|
||||
Im Quelltext steht „OHNE BANNER GIBT ES TROTZDEM EINE BUEHNE --
|
||||
dann aus Farbe". Auf dem Bild vom 07.10.2026 war davon nichts zu
|
||||
sehen: Die Karte ohne Bild fing direkt mit dem Titel an. */
|
||||
const buehnenLage = await seite.evaluate(() => {
|
||||
const raus = [];
|
||||
for (const k of document.querySelectorAll('.eintrag-karte[data-eventkarte="ja"]')) {
|
||||
const b = k.querySelector(".ev-buehne");
|
||||
raus.push({
|
||||
titel: (k.querySelector(".eintrag-titel")?.textContent || "?").slice(0, 16),
|
||||
mitbild: b?.dataset.mitbild || "nein",
|
||||
buehneHoehe: b ? Math.round(b.getBoundingClientRect().height) : 0,
|
||||
buehneGrund: b ? getComputedStyle(b).backgroundImage.slice(0, 30) : "-",
|
||||
marke: (() => { const m = k.querySelector(".marke-art"); if (!m) return null;
|
||||
const r = m.getBoundingClientRect(); const st = getComputedStyle(m);
|
||||
return { text: m.textContent.trim(), b: Math.round(r.width), h: Math.round(r.height),
|
||||
farbe: st.color, grund: st.backgroundColor, sicht: st.visibility, deck: st.opacity,
|
||||
yImKarton: Math.round(r.top - k.getBoundingClientRect().top),
|
||||
xImKarton: Math.round(r.left - k.getBoundingClientRect().left) }; })(),
|
||||
});
|
||||
}
|
||||
return raus;
|
||||
});
|
||||
melde(" -- Buehnen: " + JSON.stringify(buehnenLage));
|
||||
|
||||
/* ===== NICHTS DARF AUS DER KARTE HERAUSLAUFEN (07.10.2026) ========
|
||||
|
||||
Die Karte traegt ein `clip-path` -- was oben herausragt, ist nicht
|
||||
verschoben, sondern WEG. Genau das ist am 07.10.2026 passiert, als
|
||||
die Karten halb so breit wurden: Das Etikett „Agentur-Events" sass
|
||||
auf der Karte „September Rueckblick" bei y = -28 px.
|
||||
|
||||
WARUM DIE ALTE MESSUNG ES NICHT FAND: Sie fragte nach Farbe,
|
||||
Groesse, Sichtbarkeit und Deckkraft -- und bekam auf alles die
|
||||
richtige Antwort. Nach der LAGE hatte sie nie gefragt. Ein Element
|
||||
kann vollstaendig „sichtbar" sein und trotzdem ausserhalb des
|
||||
Bildes liegen.
|
||||
|
||||
Deshalb jetzt fuer JEDES Stueck jeder Eventkarte, nicht nur fuer
|
||||
das Etikett: Die naechste Sache, die oben herausfaellt, ist
|
||||
bestimmt eine andere. */
|
||||
const ueberstand = await seite.evaluate(() => {
|
||||
const raus = [];
|
||||
for (const k of document.querySelectorAll('.eintrag-karte[data-eventkarte="ja"]')) {
|
||||
const kr = k.getBoundingClientRect();
|
||||
for (const t of k.querySelectorAll("*")) {
|
||||
const r = t.getBoundingClientRect();
|
||||
if (r.width === 0 && r.height === 0) continue;
|
||||
if (r.top < kr.top - 1 || r.left < kr.left - 1 || r.right > kr.right + 1) {
|
||||
raus.push(`${(k.querySelector(".eintrag-titel")?.textContent || "?").slice(0, 14)}: `
|
||||
+ `${(t.className || t.tagName).toString().split(" ")[0]} `
|
||||
+ `oben ${Math.round(r.top - kr.top)}px / links ${Math.round(r.left - kr.left)}px`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return raus;
|
||||
});
|
||||
const geprueftTeile = await seite.evaluate(() =>
|
||||
[...document.querySelectorAll('.eintrag-karte[data-eventkarte="ja"] *')].length);
|
||||
/* DIE ZAHL STEHT IN DER BEDINGUNG: "nichts ragt heraus" waere auf
|
||||
einer leeren Liste ebenfalls wahr. */
|
||||
ok(geprueftTeile > 30 && ueberstand.length === 0,
|
||||
`keines der ${geprueftTeile} Teile ragt aus seiner Karte heraus (${ueberstand.slice(0, 2).join(" | ") || "keins"})`);
|
||||
|
||||
/* ===== UND DIE BUEHNE DARF NICHT GEQUETSCHT WERDEN ================
|
||||
|
||||
Dieselbe Sache eine Ebene tiefer: Die Buehne ist ein Flex-Element
|
||||
der Karte und darf deshalb schrumpfen. Tut sie das, laeuft ihr
|
||||
Inhalt unten heraus -- im Bildschirmfoto vom 07.10.2026 lag der
|
||||
Zeitbalken auf dem Satz „Beschreibung zum Test." darunter.
|
||||
|
||||
Gemessen wird der Inhalt gegen den Kasten, nicht das Aussehen:
|
||||
`scrollHeight > clientHeight` heisst „es passt nicht hinein", egal
|
||||
ob man es sieht oder nicht. */
|
||||
const gequetscht = await seite.evaluate(() =>
|
||||
[...document.querySelectorAll('.eintrag-karte[data-eventkarte="ja"] .ev-buehne')]
|
||||
.map((b) => ({
|
||||
titel: (b.closest(".eintrag-karte").querySelector(".eintrag-titel")?.textContent || "?").slice(0, 14),
|
||||
innen: b.scrollHeight, kasten: b.clientHeight,
|
||||
stil: (() => {
|
||||
const st = getComputedStyle(b);
|
||||
return { h: st.height, maxH: st.maxHeight, minH: st.minHeight,
|
||||
flex: st.flex, ar: st.aspectRatio, pad: st.paddingTop + "/" + st.paddingBottom };
|
||||
})(),
|
||||
kinder: [...b.children].map((c) =>
|
||||
(c.className || c.tagName).toString().split(" ")[0] + ":"
|
||||
+ Math.round(c.getBoundingClientRect().height)),
|
||||
})));
|
||||
melde(" -- Buehnen-Inhalt: " + JSON.stringify(gequetscht));
|
||||
ok(gequetscht.length >= 3 && gequetscht.every((b) => b.innen <= b.kasten + 1),
|
||||
`der Inhalt passt in jede der ${gequetscht.length} Buehnen `
|
||||
+ `(${gequetscht.map((b) => `${b.titel} ${b.innen}/${b.kasten}`).join(", ")})`);
|
||||
|
||||
const tonListe = Object.values(toene);
|
||||
ok(tonListe.length >= 3 && new Set(tonListe).size === tonListe.length,
|
||||
`jeder Zustand traegt einen eigenen Ton (${JSON.stringify(toene)})`);
|
||||
|
||||
/* Zum PAAR scrollen, nicht einfach den Bildschirm fotografieren: Oben
|
||||
stehen Kopf, Filter und die Vorschlaege -- auf einem Bild davon
|
||||
waere von den zwei Karten nebeneinander nichts zu sehen. */
|
||||
await seite.evaluate(() => {
|
||||
const k = [...document.querySelectorAll('.eintrag-karte[data-eventkarte="ja"]')];
|
||||
const paar = k.find((a, i) => k.slice(i + 1).some((b2) =>
|
||||
Math.abs(a.getBoundingClientRect().top - b2.getBoundingClientRect().top) < 5));
|
||||
(paar || k[0]).scrollIntoView({ block: "start", behavior: "instant" });
|
||||
window.scrollBy(0, -24);
|
||||
});
|
||||
await seite.waitForTimeout(400);
|
||||
await seite.screenshot({ path: "server/pruef-eventkarte-raster.png", fullPage: false });
|
||||
|
||||
/* ===== „BANNER GANZ" ZEIGT ES IN DER MITTE (07.10.2026) ===========
|
||||
|
||||
Filipe: „wenn man auf banner anzeigen drückt soll das banner schön
|
||||
in der mitte sein."
|
||||
|
||||
Vorher oeffnete der Knopf die DATEI in einem neuen Tab -- also die
|
||||
eingebaute Bildanzeige des Browsers: Bild oben links, Rest schwarz.
|
||||
Jetzt die Bildschau des Hauses, die genau dafuer schon existierte
|
||||
und hier nur nie angeschlossen war. */
|
||||
const vorherAdresse = seite.url();
|
||||
const bannerKnopf = await seite.$(".ev-buehne__ganz");
|
||||
ok(!!bannerKnopf, "die Karte mit Banner hat den Knopf „Banner ganz“");
|
||||
await bannerKnopf.click();
|
||||
/* AUF DAS BILD WARTEN, nicht auf eine Zahl: Vor dem Laden ist
|
||||
`naturalWidth` null, und null mal null steht weder in der Mitte
|
||||
noch sonst wo. */
|
||||
await seite.waitForFunction(() => {
|
||||
const b = document.querySelector("dialog.bildschau .bildschau__bild");
|
||||
return b && b.naturalWidth > 0;
|
||||
}, null, { timeout: 15000 }).catch(() => {});
|
||||
await seite.waitForTimeout(400);
|
||||
|
||||
const schau = await seite.evaluate(() => {
|
||||
const d = document.querySelector("dialog.bildschau");
|
||||
if (!d) return { da: false };
|
||||
const b = d.querySelector(".bildschau__bild");
|
||||
const r = b.getBoundingClientRect();
|
||||
return {
|
||||
da: true, offen: d.open,
|
||||
mitteX: Math.round(r.x + r.width / 2), mitteY: Math.round(r.y + r.height / 2),
|
||||
fensterX: Math.round(window.innerWidth / 2), fensterY: Math.round(window.innerHeight / 2),
|
||||
breite: Math.round(r.width), hoehe: Math.round(r.height),
|
||||
grund: getComputedStyle(d.querySelector(".bildschau__grund")).backgroundImage.slice(0, 22),
|
||||
};
|
||||
});
|
||||
melde(" -- Bildschau: " + JSON.stringify(schau));
|
||||
ok(schau.da && schau.offen, `die Bildschau geht auf (${schau.da ? "offen=" + schau.offen : "gar nicht da"})`);
|
||||
/* IN DER MITTE, in Pixeln gemessen. Waagerecht genau, senkrecht mit
|
||||
etwas mehr Spiel: Oben liegt die Leiste mit Namen und Knoepfen,
|
||||
das Bild sitzt deshalb ein Stueck tiefer als die halbe Hoehe. */
|
||||
ok(schau.da && Math.abs(schau.mitteX - schau.fensterX) <= 8,
|
||||
`das Banner steht waagerecht in der Mitte (${schau.mitteX} gegen ${schau.fensterX})`);
|
||||
ok(schau.da && Math.abs(schau.mitteY - schau.fensterY) <= 60,
|
||||
`und senkrecht ebenfalls (${schau.mitteY} gegen ${schau.fensterY})`);
|
||||
ok(schau.da && schau.breite > 300 && schau.hoehe > 100,
|
||||
`es ist wirklich gross (${schau.breite}x${schau.hoehe} px)`);
|
||||
ok(schau.da && schau.grund.includes("gradient"),
|
||||
`mit dem Hintergrund der Schau statt schwarzer Leere (${schau.grund}…)`);
|
||||
/* GEGENPROBE ZUM ABFANGEN: Haette der Klick NICHT gegriffen, waere
|
||||
der Browser zur Bilddatei gesprungen. Dass die Adresse dieselbe
|
||||
ist, beweist, dass die Schau den Klick wirklich uebernommen hat --
|
||||
und nicht nur zufaellig irgendwo im Dokument liegt. */
|
||||
ok(seite.url() === vorherAdresse,
|
||||
`die Seite ist dabei nicht weggesprungen (${seite.url() === vorherAdresse ? "dieselbe Adresse" : seite.url()})`);
|
||||
|
||||
await seite.screenshot({ path: "server/pruef-eventkarte-schau.png", fullPage: false });
|
||||
await seite.keyboard.press("Escape");
|
||||
await seite.waitForTimeout(300);
|
||||
const zu = await seite.evaluate(() =>
|
||||
!document.querySelector("dialog.bildschau")?.open);
|
||||
ok(zu, "und Escape schliesst sie wieder");
|
||||
|
||||
/* DIE ZAHLEN AUS DEM BEFUND, jede einzeln. */
|
||||
ok(masse.titelInKarte < 180,
|
||||
`der Titel steht ${masse.titelInKarte} px unter der Kartenkante (vorher 638+, Grenze 180)`);
|
||||
@@ -758,6 +1004,30 @@ melde("\n=== 7. Auf dem Handy (412 px) ===");
|
||||
melde(" -- gemessen: " + JSON.stringify(handy));
|
||||
ok(handy.knopfHoehe >= 44,
|
||||
`das Beruehrziel ist ${handy.knopfHoehe} px hoch (Grenze 44)`);
|
||||
|
||||
/* ===== AUF DEM HANDY STEHT WIEDER EINE JE REIHE (07.10.2026) ======
|
||||
|
||||
Die Gegenprobe zum Raster: Zwei Karten nebeneinander sind am
|
||||
Rechner richtig und auf 412 px unbedienbar -- dort waere jede
|
||||
199 px breit. Dass die Regel UMSCHALTET, muss gemessen werden;
|
||||
sonst beweist „zwei nebeneinander" am Rechner nur, dass es dort
|
||||
passt, und nichts darueber, was auf dem Telefon passiert. */
|
||||
const schmal = await seite.evaluate(() => {
|
||||
const k = [...document.querySelectorAll('.eintrag-karte[data-eventkarte="ja"]')]
|
||||
.map((x) => { const r = x.getBoundingClientRect(); return { x: Math.round(r.x), y: Math.round(r.y), b: Math.round(r.width) }; });
|
||||
return {
|
||||
karten: k,
|
||||
/* Stehen zwei auf gleicher Hoehe? Dann waere nicht umgeschaltet. */
|
||||
paar: k.some((a, i) => k.slice(i + 1).some((b2) => Math.abs(a.y - b2.y) < 5)),
|
||||
quer: document.documentElement.scrollWidth - document.documentElement.clientWidth,
|
||||
};
|
||||
});
|
||||
ok(schmal.karten.length >= 2 && !schmal.paar,
|
||||
`auf 412 px steht eine Karte je Reihe (${schmal.karten.map((a) => `${a.b}px@${a.y}`).join(", ")})`);
|
||||
ok(schmal.karten.every((a) => a.b > 330),
|
||||
`und sie ist breit genug zum Bedienen (${schmal.karten.map((a) => a.b).join(", ")} px)`);
|
||||
ok(schmal.quer <= 1,
|
||||
`die Seite laesst sich nicht seitlich schieben (${schmal.quer} px)`);
|
||||
/* UND WENN DOCH, DANN WELCHES ELEMENT? Eine Zahl allein schickt
|
||||
einen auf die Suche; der Schuldige steht im DOM und wird nur nicht
|
||||
genannt. (Projektgedaechtnis: eine Pruefung, die nicht sagen kann,
|
||||
|
||||
Reference in New Issue
Block a user