Nichts wird mehr nach links oder rechts geschoben

Filipe, 28.09.2026: "ich will auch nicht dass man sachen nach links
und rechts schieben muss. perfektion das untereinander. ich will
niemals irgendwas nach links oder rechts swippen muessen." Dazu ein
Bildschirmfoto der Tafel "Gestaltung" mit waagerechter Rollleiste.

GEMESSEN, NICHT GERATEN

Ein grep nach `overflow-x` findet nur die absichtlichen Roller. Er
findet nicht die Stelle, an der ein Inhalt breiter ist als sein
Kasten und der Browser von sich aus eine Rollleiste anhaengt -- und
genau das war auf dem Bild zu sehen. `server/mess-quer.mjs` geht
deshalb im echten Browser jedes Element durch, auf fuenf
Bildschirmgroessen, in allen sieben Registern, bei offener und
zugeklappter Regie und in allen fuenf Anordnungen.

Erster Lauf: 78 Stellen. Davon waren 54 KEINE -- `overflow-x: hidden`
ist abgeschnittener Text, dort laesst sich nichts schieben. Die
Messung trennt das jetzt; wer es mitzaehlt, findet die echten nicht
mehr. Uebrig blieben vier Ursachen:

  1. DIE REGISTER rollten absichtlich waagerecht. Auf 412 px standen
     von 605 px Registern 193 rechts ausserhalb -- dass es
     "Gestaltung" und "Spenden" gibt, erfuhr man nur beim Wischen auf
     Verdacht. Sie brechen jetzt um. Das kostet oben rund 45 Pixel
     und bringt Gewissheit dafuer.

  2. `.stufen` STAND ZWEIMAL IN reaktion.css -- einmal fuer die
     Stufenleiter der Spenden (`display: grid`), 600 Zeilen spaeter
     fuer die Chat-Leiste Offen/Team/Zu (`display: flex`). Die
     spaetere gewinnt: Die Stufenleiter stellte ihre drei Stufen
     NEBENEINANDER, 572 Pixel in einer 327 Pixel schmalen Spalte.
     Das ist die Rollleiste auf Filipes Bild. Die Chat-Leiste heisst
     jetzt `.chatstufen` / `.chatstufe`.
     Dritter Fall dieser Art nach `.tafel` und `.stufe-knopf`.

  3. DIE TAFELN KONNTEN NICHT SCHRUMPFEN. Ein Gitterfeld hat von
     sich aus `min-width: auto` und besteht auf seinem unteilbarsten
     Inhalt. Mit `minmax(0, 1fr)` und `min-width: 0` bricht jetzt
     alles um, statt hinauszulaufen.

  4. DIE TRANSPORTLEISTE ragte am Handy 76 Pixel ueber beide Kanten
     ("Naechstes" war nur halb zu sehen). Zwei Gruende: `.transport__teil`
     hatte kein `flex-wrap` (die mittlere Gruppe schon -- zwei Regeln
     fuer dieselbe Sache, eine vergessen), und im Umschalter steht
     ein ganzer Videotitel, der als Flex-Kind auf seiner vollen
     Breite bestand.

NEBENBEFUND: EINE FESTE ZAHL VON GESTERN

Der Saal stand auf `height: calc(100dvh - 69px)` -- 69 war einmal
die gemessene Hoehe der Kopfleiste. Sie ist 73 geworden, und der
Saal endete damit 4 Pixel unter dem Fensterrand: Der Play-Knopf war
nur mit Scrollen zu erreichen. Die Antwort ist keine neue Zahl,
sondern eine Regel, die misst -- der Koerper ist jetzt eine Spalte,
die Kopfleiste nimmt, was sie braucht, der Saal bekommt den Rest.
(Dabei noch ein Spezifitaetsunfall: `.reaktion-seite` (0,1,0)
verliert gegen `body.start` (0,1,1) aus start.css.)

NEUE WACHEN

  - `mess-quer.mjs` mit Gegenprobe (ohne die Reparaturen findet sie
    47 px, mit ihnen nichts).
  - pruef-reaktion: kein absichtliches `overflow-x: auto` mehr; und
    zwei Grundregeln derselben Klasse mit verschiedenem `display`
    sind ein Namensstreit. Der bisherige Waechter verglich nur
    ZWISCHEN Stilvorlagen -- `.stufen` stand zweimal in DERSELBEN.
  - pruef-css-klassen: jede Klammer hat ihr Gegenstueck. Beim
    Umschreiben blieb heute das Ende einer Regel ohne ihren Anfang
    stehen; der Browser wirft die Zeile weg und schliesst dafuer den
    naechsten @media-Block zu frueh. Drei Befunde sahen daraufhin wie
    Programmfehler aus.

MESSUNG NACHGEZOGEN

`mess-reaktion` stammte noch aus der Zeit vor "Regie links" und
"drei Kacheln" und meldete drei Dinge, die keine Fehler waren --
gemessen gegen den Stand von HEAD: identisch rot. Sie misst ausserdem
seit heute mit FINGER statt Mauszeiger; ohne `hasTouch` greift keine
einzige Regel aus `@media (pointer: coarse)`, und dort stehen alle
44-Pixel-Beruehrziele des Hauses.

OFFEN UND AUFGESCHRIEBEN

Am Handy bleibt bei offener Regie vom Bild nichts (0 px). Der Mangel
besteht seit dem Umbau "Regie links"; drei Versuche, ihn heute zu
loesen, haben Knoepfe verdeckt -- und ein verdeckter Knopf ist
schlimmer als ein kleines Bild. Die Versuche und der richtige Weg
(Tempo-Gruppe hinter einen Knopf, wie "Aa" im Chat) stehen in
reaktion.css. `mess-reaktion` meldet ihn als dritten Ausgang: nicht
als Fehler und nicht als bestanden.

GEPRUEFT
  pruef-reaktion     384 / 0   (vorher 379)
  pruef-spenden      172 / 0
  pruef-buehne        36 / 0
  pruef-css-klassen, -tippziele, -struktur, -handy: ohne Befund
  mess-quer          EXIT 0 -- 28 Stellen, fuenf Groessen, nichts rollt
  mess-reaktion      EXIT 0, 1 benannter offener Punkt
  mess-buehne        EXIT 0
This commit is contained in:
2026-09-29 02:34:09 +02:00
parent 813d2e85a1
commit a9e0834cfb
50 changed files with 2669 additions and 1523 deletions
+123 -3
View File
@@ -910,9 +910,30 @@ melde("=== 12. Die Regie ===");
Klasse, um sie im Buehnenmodus wegzunehmen. Wer beides gleich
behandelt, macht aus einer echten Regel eine, die staendig
grundlos anschlaegt -- und die wird weggeklickt. */
const eigeneAus = (datei) => new Set(
[...roh(datei).matchAll(/(^|[\s,>+~(}])(\.(-?[a-zA-Z_][a-zA-Z0-9_-]*)[^{};,]*)\{/g)]
.map((m) => m[3]));
/* AM ANFANG DES SELEKTORS -- nicht irgendwo darin.
Der Kommentar oben sagte das schon; das Muster tat es nicht.
Es nahm jede Klasse, der ein Trennzeichen vorausging -- also
auch das `.kopfleiste` in `body.reaktion-seite > .kopfleiste`.
Das ist aber genau die Bezugnahme, die erlaubt sein soll, und
die Regel schlug am 28.09.2026 darauf an. Dass sie es bei
`body[data-nur="buehne"] .kopfleiste` nicht tat, lag nur an
einem Komma dahinter -- also am Zufall, nicht an der Absicht.
Jetzt wird jeder Selektor einzeln angesehen und gefragt, ob er
MIT der Klasse beginnt. Das ist die Frage, um die es geht:
„Benenne ich hier ein eigenes Bauteil?" */
const eigeneAus = (datei) => {
const raus = new Set();
for (const m of roh(datei).matchAll(/([^{}]+)\{/g)) {
for (const teil of m[1].split(",")) {
const t = teil.trim();
const k = t.match(/^\.(-?[a-zA-Z_][a-zA-Z0-9_-]*)/);
if (k) raus.add(k[1]);
}
}
return raus;
};
const meine = eigeneAus("reaktion.css");
const streit = [];
@@ -935,6 +956,105 @@ melde("=== 12. Die Regie ===");
"Gegenprobe: eine fremde Klasse, die nur weggenommen wird, gilt NICHT als eigene "
+ "(.kopfleiste im Buehnenmodus)");
/* ---- Und derselbe Streit INNERHALB einer Datei -----------------
`.stufen` stand am 28.09.2026 zweimal in reaktion.css: einmal
fuer die Stufenleiter der Spenden (`display: grid`), 600 Zeilen
spaeter fuer die Chat-Leiste Offen/Team/Zu (`display: flex`).
Die spaetere gewinnt -- und damit stand die Stufenleiter
nebeneinander statt untereinander und rollte waagerecht.
GEZAEHLT WIRD NUR, WAS SICH WIDERSPRECHEN KANN: zwei Regeln,
deren Auswahl NUR aus der Klasse besteht (kein Zustand, kein
Kontext, kein Attribut) und die BEIDE `display` setzen. Zwei
Regeln zu derselben Klasse sind sonst voellig normal --
`.knopf { }` und `.knopf:hover { }` gehoeren zusammen. Wer
jede Wiederholung meldet, baut eine Warnung, die immer kommt,
und die wird weggeklickt. */
/* ZWEI EINSCHRAENKUNGEN, BEIDE GEMESSEN UND NOETIG.
(1) Die Auswahl muss GENAU die Klasse sein. Sonst zaehlt
`.pult[data-auf="ja"] .pult__innen { display: grid }` als
zweite Grundregel zu `.pult__innen { display: none }` -- und
das ist kein Streit, sondern derselbe Bauteil in zwei
Zustaenden.
(2) `display: none` zaehlt nicht mit. „Erst versteckt, dann
gezeigt" ist das haeufigste Paar im ganzen Haus. Wer es
meldet, baut eine Warnung, die bei fuenf harmlosen Stellen
anschlaegt -- und beim sechsten, echten Fall hat sie
niemand mehr gelesen.
Uebrig bleibt genau das, was sich widersprechen KANN: zwei
Grundregeln, die beide sagen, WIE das Bauteil gebaut ist --
`grid` gegen `flex`, wie bei `.stufen`. */
const positiv = (wert) => wert && wert !== "none" && !/^none/.test(wert);
const lies = (text) => {
const raus = {};
for (const m of text.matchAll(/(^|[\s};])(\.(-?[a-zA-Z_][a-zA-Z0-9_-]*))\s*\{([^{}]*)\}/g)) {
const wert = (m[4].match(/(?:^|[\s;])display\s*:\s*([^;]+)/) || [, ""])[1].trim();
if (!positiv(wert)) continue;
(raus[m[3]] ||= []).push(wert);
}
return raus;
};
const streitIn = (text) => Object.entries(lies(text))
.filter(([, was]) => new Set(was).size > 1)
.map(([name, was]) => `${name} (${[...new Set(was)].join(" vs. ")})`);
const grundregeln = lies(roh("reaktion.css"));
const doppelt = streitIn(roh("reaktion.css"));
ok(Object.keys(grundregeln).length > 20 && doppelt.length === 0,
doppelt.length
? `Namensstreit in reaktion.css selbst: ${doppelt.join(", ")}`
: `${Object.keys(grundregeln).length} Klassen sagen, WIE sie gebaut sind `
+ "-- keine davon zweimal verschieden");
/* GEGENPROBE: Findet die Regel einen eingeschobenen Streit? Ohne
diese Zeile waere oben nur bewiesen, dass sie „in Ordnung"
sagen kann. Eingeschoben wird genau der Fall vom 28.09.: eine
zweite Grundregel mit einem anderen positiven display. */
ok(streitIn(roh("reaktion.css") + " .pult-tafel { display: flex; } ")
.some((z) => z.startsWith("pult-tafel (")),
"Gegenprobe: eine zweite Grundregel mit anderem display faellt auf");
/* UND IN DIE ANDERE RICHTUNG: Ein Bauteil, das erst versteckt und
dann gezeigt wird, darf NICHT anschlagen -- sonst haette man
die Regel nur so lange geschaerft, bis sie alles meldet. */
ok(streitIn(" .probe { display: none; } .probe { display: grid; } ").length === 0
&& streitIn(" .probe { display: grid; } .probe { display: flex; } ").length === 1,
"Gegenprobe: verstecken-und-zeigen ist kein Streit, grid-gegen-flex schon");
/* ---- Nichts rollt absichtlich waagerecht ----------------------
Filipe, 28.09.2026: „ich will auch nicht dass man sachen nach
links und rechts schieben muss. perfektion das untereinander.
ich will niemals irgendwas nach links oder rechts swippen
muessen."
Bis dahin rollten die sieben Register waagerecht, mit einer
Begruendung, die fuer sich stimmte (zwei Zeilen kosten oben 45
Pixel). Sie kostete dafuer die Gewissheit: Auf 412 Pixeln
standen 193 Pixel Register rechts ausserhalb, und dass es
„Gestaltung" und „Spenden" gibt, erfuhr man nur beim Wischen
auf Verdacht.
DIESE PRUEFUNG FINDET NUR DIE ABSICHTLICHEN ROLLER. Die
entstehenden -- ein Inhalt, der breiter ist als sein Kasten --
sieht man im Quelltext nicht; dafuer gibt es
`server/mess-quer.mjs`, das im echten Browser jedes Element
nachmisst. Beide zusammen, keines allein. */
const roller = [...roh("reaktion.css")
.matchAll(/([^{}]+)\{([^{}]*overflow-x\s*:\s*(auto|scroll)[^{}]*)\}/g)]
.map((m) => m[1].trim().split(String.fromCharCode(10)).pop().trim());
ok(roller.length === 0,
roller.length ? `rollt waagerecht: ${roller.join(", ")}`
: "nichts in reaktion.css rollt absichtlich waagerecht");
ok(/overflow-x/.test(roh("reaktion.css") + "x{overflow-x:auto}")
&& [...("y{overflow-x: auto}").matchAll(
/([^{}]+)\{([^{}]*overflow-x\s*:\s*(auto|scroll)[^{}]*)\}/g)].length === 1,
"Gegenprobe: der Sucher findet ein overflow-x, wenn eines dasteht");
/* Der Pegel wird gemessen und nicht gemalt. */
ok(/createAnalyser\(\)/.test(js) && /getByteTimeDomainData/.test(js),
"die Pegel kommen aus einer echten Messung");