Die Kachelfarben kommen jetzt WIRKLICH auf dem Bildschirm an

Filipe: "ich raste aus im ernst, was verstehst du nicht darunter wenn
ich sage das alle kacheln eine andere farbe haben sollen ich seh
immer wieder sehr viele die einfach komplett die gleiche farbe haben."

ER HATTE RECHT, UND MEINE PRUEFUNG WAR TROTZDEM GRUEN.
Der Grund war ein Messfehler, und zwar meiner: pruef-kachelfarben las
`--ton` aus start.css -- die ROHFARBE. Die lag zwischen allen 31
Kacheln weit auseinander (0,0978 in OKLab). Nur steht sie so nie auf
dem Bildschirm.

GEMESSEN an echten Bildpunkten (neu: tools/kachel-echtfarbe.mjs):
  Rohfarben:              0,0978 auseinander
  Auf dem Bildschirm:     0,0141
  Es kamen an:            14,4 Prozent
  Fuer ein Auge gleich:   ACHT Paare

Drei Stellen haben die Farbe geschluckt, alle in .kachel:
  1. Eine Wolke mit 16 Prozent -- in immer DEMSELBEN Blau, auf jeder
     Kachel. Der staerkste Gleichmacher der ganzen Wand. Sie traegt
     jetzt den Ton der Kachel.
  2. Die Vignette legte bis zu 55 Prozent Dunkelblau darueber.
  3. Der Grund trug den Ton zu 24 Prozent und ging ab 58 Prozent in
     fast reines Schwarz. Zwei Drittel jeder Kachel waren damit
     dieselbe Farbe, egal welcher Ton darueber stand.

Danach: 51,1 Prozent kamen an, NULL ununterscheidbare Paare.

UND DANN HAETTE ICH EINEN FEHLER GEGEN EINEN ANDEREN GETAUSCHT.
Gemessen an jedem einzelnen Kacheltext: VORHER war 1 von 31 unter
4,5:1, DANACH 27 von 31. Also genau das, was Filipe schon zweimal
gemeldet hat ("man bekommt fast nichts gelesen"). Kein Messwert hat
es angezeigt -- pruef-buehne nimmt Stichproben und traf die
Kacheltexte nicht.

Behoben mit einem Lesesaum: ein dunkler Verlauf VON UNTEN, der genau
den Streifen abdunkelt, auf dem gelesen wird, und die oberen zwei
Drittel in Ruhe laesst. Dasselbe Mittel, mit dem jede Bildunterschrift
auf einem Foto lesbar gemacht wird.

  Texte unter 4,5:1:   1 -> 27 -> 0
  Farbe kommt an:      14,4 % -> 51,1 % -> 36,5 %

VERTRAULICH MELDEN IST JETZT KNALLROT.
Filipe: "nicht die kachel draussen soll rot sein sondern die kachel
vertraulich melden nur die soll knall rot sein!!!" Ich hatte screen5
und screen6 vertauscht. Ringtausch ohne neue Farbe:
  Vertraulich melden  28 -> 38  (#ff1a1a, knallrot)
  Draussen            38 -> 39  (die Farbe, die Regeln & Hilfe hatte)
  Regeln & Hilfe      39 -> 28  (das frei gewordene Gruen)

NEU, UND DER EIGENTLICHE LERNEFFEKT:
server/helfer-kachel-echtfarbe.mjs misst beides in EINER Messung --
Farbunterschied UND Lesbarkeit. Wer sie trennt, verbessert das eine
und verliert das andere; genau das ist heute passiert. Benutzt von
der Pruefung und vom Werkzeug, damit es nur eine Fassung gibt.

pruef-kachelfarben 18/0 (war 13), davon fuenf am echten Bildschirm:
  - kein Paar sieht auf dem Bildschirm gleich aus (0,0357)
  - mindestens ein Drittel der Rohfarbe kommt an (36,5 %)
  - jeder Kacheltext erreicht 4,5:1 (schlechtester: 4,80:1)
Mit drittem Ausgang: kein Browser heisst "konnte nicht nachsehen",
nicht "in Ordnung".

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-09-22 10:26:49 +02:00
co-authored by Claude Opus 5
parent 65d278a18e
commit e12392a289
44 changed files with 1205 additions and 559 deletions
+300
View File
@@ -0,0 +1,300 @@
/* =====================================================================
helfer-kachel-echtfarbe.mjs — welche Farbe eine Kachel WIRKLICH hat
---------------------------------------------------------------------
Filipe am 22.09.2026: „ich seh immer wieder sehr viele die einfach
komplett die gleiche farbe haben ich will dass sie wirklich alle
alle alle komplett unterschiedlich sind."
Er hatte recht, und `pruef-kachelfarben` war trotzdem grün. Der
Grund war ein Messfehler: Sie las `--ton` aus `start.css` — die
ROHFARBE. Die lag zwischen allen 31 Kacheln weit auseinander
(0,0978 in OKLab). Nur steht sie so nie auf dem Bildschirm: Die
Kachel zeichnet sie als Verlauf unter einem Sternenfeld, einer
Vignette und (bis zu diesem Tag) einer fest blauen Wolke.
Gemessen kamen 14,4 Prozent an. Acht Paare waren für ein Auge
nicht mehr zu unterscheiden.
WARUM DER DURCHSCHNITT ÜBER DIE GANZE KACHEL
Eine Einzelmessung an einem Punkt träfe vielleicht einen Stern,
einen Rand oder das Zeichen. Gemittelt wird über alle Bildpunkte —
das ist ungefähr das, was ein Auge aus zwei Metern Entfernung
sieht, und genau darum geht es.
WARUM DAS HIER STEHT UND NICHT ZWEIMAL
`tools/kachel-echtfarbe.mjs` zeigt die Liste zum Ansehen,
`server/pruef-kachelfarben.mjs` prüft sie. Zwei Fassungen derselben
Messung altern unterschiedlich — dann sagt die eine „gleich" und
die andere „verschieden", und man glaubt der bequemeren.
===================================================================== */
import { mkdtempSync, rmSync, readFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";
import { spawn, execFileSync } from "node:child_process";
import { DatabaseSync } from "node:sqlite";
import { scryptSync, randomBytes } from "node:crypto";
import { createServer } from "node:https";
import { request as httpAnfrage } from "node:http";
const HIER = dirname(fileURLToPath(import.meta.url));
const CREW = "crew.dogfather-universe.com";
/* ---- Farbrechnung (OKLab, wie in den Farbwerkzeugen) --------------- */
const linear = (c) => (c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4);
export function oklab([R, G, B]) {
const [r, g, b] = [R / 255, G / 255, B / 255].map(linear);
const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);
const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);
const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);
return [
0.2104542553 * l + 0.7936177850 * m - 0.0040720468 * s,
1.9779984951 * l - 2.4285922050 * m + 0.4505937099 * s,
0.0259040371 * l + 0.7827717662 * m - 0.8086757660 * s,
];
}
const abstand = (a, b) => Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2]);
const ausHex = (hex) => (/^#[0-9a-f]{6}$/i.test(hex)
? [1, 3, 5].map((i) => parseInt(hex.slice(i, i + 2), 16)) : null);
export const alsHex = (c) => "#" + c.map((x) => x.toString(16).padStart(2, "0")).join("");
/* UNTER 0,02 SIEHT EIN AUGE KEINEN UNTERSCHIED MEHR.
In OKLab entspricht 1 Einheit ungefähr dem ganzen Helligkeitsbereich;
0,02 ist damit die Grenze, ab der zwei Flächen nebeneinander noch
als „zwei Farben" gelesen werden. Darunter ist es eine. */
export const NICHT_UNTERSCHEIDBAR = 0.02;
/**
* Fotografiert die Kachelwand und liest die echten Bildpunkte aus.
*
* Gibt IMMER ein Ergebnis zurück — auch wenn es nicht messen konnte.
* `moeglich: false` mit Grund ist der dritte Ausgang: nicht „in
* Ordnung", nicht „kaputt", sondern „konnte nicht nachsehen".
*/
export async function pruefeEchteFarben({ port = 4369, breite = 1500 } = {}) {
let chromium;
try {
({ chromium } = await import("playwright-core"));
} catch {
return { moeglich: false, grund: "playwright-core ist nicht da" };
}
const ordner = mkdtempSync(join(tmpdir(), "ws-echtfarbe-"));
const DB = join(ordner, "workspace.db");
let kind = null, vorbau = null, browser = null;
try {
kind = spawn(process.execPath, [join(HIER, "index.js")], {
env: { ...process.env, WORKSPACE_DB: DB, PORT: String(port),
SITE_ACCESS_SECRET: "lokaler-test",
SITE_PUBLIC_LAUNCH_AT: "2020-01-01T00:00:00+01:00" },
stdio: ["ignore", "pipe", "pipe"] });
let oben = false;
for (let i = 0; i < 80; i++) {
await new Promise((r) => setTimeout(r, 250));
if (kind.exitCode !== null) break;
try {
const a = await fetch(`http://127.0.0.1:${port}/workspace/`, { redirect: "manual" });
if (a.status < 500) { oben = true; break; }
} catch { /* noch nicht oben */ }
}
if (!oben) return { moeglich: false, grund: `der Server kam auf Port ${port} nicht hoch` };
const d = new DatabaseSync(DB);
const salz = randomBytes(16).toString("hex");
const hash = scryptSync("CODE-DOGI-0001", salz, 64,
{ N: 32768, r: 8, p: 1, maxmem: 96 * 1024 * 1024 }).toString("hex");
d.prepare("INSERT INTO personen (name,rolle,code_hash,code_salt,code_n,aktiv,erstellt)"
+ " VALUES (?,?,?,?,?,1,?)")
.run("Filipe", "admin", hash, salz, 32768, new Date().toISOString());
d.close();
const keks = await new Promise((fertig, schief) => {
const daten = JSON.stringify({ rolle: "admin", code: "CODE-DOGI-0001" });
const a = httpAnfrage({ host: "127.0.0.1", port, path: "/workspace/api/anmelden",
method: "POST",
headers: { "content-type": "application/json", Host: CREW,
"content-length": Buffer.byteLength(daten) } }, (antwort) => {
let t = ""; antwort.on("data", (x) => { t += x; });
antwort.on("end", () => fertig((antwort.headers["set-cookie"] || [])
.join("; ").split(";")[0]));
});
a.on("error", schief); a.end(daten);
});
if (!keks) return { moeglich: false, grund: "die Anmeldung hat keinen Keks geliefert" };
/* Chromium zwingt crew.… per eingebauter HSTS-Liste auf https --
deshalb derselbe selbst unterschriebene Vorbau wie überall. */
const zKey = join(ordner, "k.pem"), zCrt = join(ordner, "z.pem");
try {
execFileSync("openssl", ["req", "-x509", "-newkey", "rsa:2048", "-nodes",
"-keyout", zKey, "-out", zCrt, "-days", "2", "-subj", `/CN=${CREW}`,
"-addext", `subjectAltName=DNS:${CREW}`], { stdio: "ignore" });
} catch {
return { moeglich: false, grund: "openssl ist nicht da (fuer den https-Vorbau)" };
}
const HTTPS_PORT = port + 1;
vorbau = createServer({ key: readFileSync(zKey), cert: readFileSync(zCrt) },
(an, ab) => {
const weiter = httpAnfrage({
host: "127.0.0.1", port, path: an.url, method: an.method,
headers: { ...an.headers, host: CREW },
}, (antwort) => { ab.writeHead(antwort.statusCode, antwort.headers); antwort.pipe(ab); });
weiter.on("error", () => { try { ab.destroy(); } catch { /* Zugabe */ } });
an.on("error", () => {});
an.pipe(weiter);
});
await new Promise((r) => vorbau.listen(HTTPS_PORT, "127.0.0.1", r));
browser = await chromium.launch({
args: [`--host-resolver-rules=MAP ${CREW} 127.0.0.1:${HTTPS_PORT}`],
ignoreHTTPSErrors: true,
});
const ctx = await browser.newContext({
viewport: { width: breite, height: 1000 }, ignoreHTTPSErrors: true });
const [n, w] = keks.split("=");
await ctx.addCookies([{ name: n, value: w, domain: CREW, path: "/" }]);
const seite = await ctx.newPage();
await seite.goto(`https://${CREW}/workspace/start.html`, { waitUntil: "networkidle" });
await seite.waitForTimeout(1200);
const beschriftung = await seite.evaluate(() => [...document.querySelectorAll(".kachel")]
.map((k) => ({
name: (k.querySelector(".kachel__name") || k).textContent.trim().split("\n")[0],
ton: getComputedStyle(k).getPropertyValue("--ton").trim(),
})));
/* WO DER TEXT STEHT -- fuer den Lesbarkeitsteil weiter unten. */
const textlagen = await seite.evaluate(() => [...document.querySelectorAll(".kachel")]
.map((k) => {
const t = k.querySelector(".kachel__unter") || k.querySelector(".kachel__name");
if (!t) return null;
const r = t.getBoundingClientRect(), kr = k.getBoundingClientRect();
return {
x: Math.round(r.x - kr.x), y: Math.round(r.y - kr.y),
b: Math.round(r.width), h: Math.round(r.height),
farbe: getComputedStyle(t).color,
};
}));
const zeiger = await seite.$$(".kachel");
const liste = [];
for (let i = 0; i < zeiger.length; i++) {
const png = (await zeiger[i].screenshot({ type: "png" })).toString("base64");
/* Der Browser zählt selbst -- ein PNG hier von Hand zu lesen
wäre ein eigenes Werkzeug, und eines mehr, das altert. */
const mittel = await seite.evaluate(async (daten) => {
const bild = new Image();
await new Promise((f) => { bild.onload = f; bild.src = "data:image/png;base64," + daten; });
const c = document.createElement("canvas");
c.width = bild.width; c.height = bild.height;
const g = c.getContext("2d");
g.drawImage(bild, 0, 0);
const p = g.getImageData(0, 0, c.width, c.height).data;
let r = 0, gr = 0, bl = 0, z = 0;
for (let k = 0; k < p.length; k += 4) { r += p[k]; gr += p[k + 1]; bl += p[k + 2]; z++; }
return [Math.round(r / z), Math.round(gr / z), Math.round(bl / z)];
}, png);
/* WIE LESBAR IST DER TEXT DARAUF?
============================================================
Das ist die Frage, die am 22.09.2026 beinahe untergegangen
waere. Die Farben kraeftiger zu machen war richtig -- gemessen
stieg der Anteil, der ankommt, von 14,4 auf 51,1 Prozent.
Nur waren damit 27 von 31 Kacheltexten unter 4,5:1; vorher
war es EINER. Ein Fehler gegen einen anderen getauscht, und
zwar genau der, den Filipe schon zweimal gemeldet hat ("man
bekommt fast nichts gelesen").
Gemessen wird der HAEUFIGSTE Farbwert hinter dem Text, nicht
der hellste: Der hellste Punkt IST der Text (ein erster Anlauf
meldete darum bei allen 31 einen Kontrast von 1,00 -- also
einen unmoeglichen Wert). Der Text deckt nur einen Bruchteil
der Flaeche; der haeufigste Wert ist der Grund. */
const l = textlagen[i];
let kontrast = null;
if (l && l.b > 4 && l.h > 4) {
const grund = await seite.evaluate(async ([daten, lage]) => {
const bild = new Image();
await new Promise((f) => { bild.onload = f; bild.src = "data:image/png;base64," + daten; });
const c = document.createElement("canvas");
c.width = bild.width; c.height = bild.height;
const g = c.getContext("2d");
g.drawImage(bild, 0, 0);
const x = Math.max(0, lage.x), y = Math.max(0, lage.y);
const bb = Math.min(lage.b, c.width - x), hh = Math.min(lage.h, c.height - y);
if (bb <= 0 || hh <= 0) return null;
const p = g.getImageData(x, y, bb, hh).data;
const zaehler = new Map();
for (let k = 0; k < p.length; k += 4) {
const sch = ((p[k] >> 3) << 10) | ((p[k + 1] >> 3) << 5) | (p[k + 2] >> 3);
zaehler.set(sch, (zaehler.get(sch) || 0) + 1);
}
let haeufig = 0, wie = -1;
for (const [sch, n2] of zaehler) if (n2 > wie) { wie = n2; haeufig = sch; }
return [((haeufig >> 10) & 31) << 3, ((haeufig >> 5) & 31) << 3, (haeufig & 31) << 3];
}, [png, l]);
if (grund) {
const tf = (l.farbe.match(/\d+/g) || [255, 255, 255]).slice(0, 3).map(Number);
const hell = (c) => {
const [r, g2, b] = c.map((x) => {
const v = x / 255;
return v <= 0.04045 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4;
});
return 0.2126 * r + 0.7152 * g2 + 0.0722 * b;
};
const [a1, a2] = hell(tf) > hell(grund) ? [hell(tf), hell(grund)] : [hell(grund), hell(tf)];
kontrast = (a1 + 0.05) / (a2 + 0.05);
}
}
liste.push({ ...beschriftung[i], echt: mittel, kontrast });
}
/* ---- Auswerten ---------------------------------------------- */
let engstes = { d: Infinity, a: "", b: "" };
let engstesRoh = Infinity;
let ununterscheidbar = 0, paare = 0;
for (let i = 0; i < liste.length; i++) {
for (let j = i + 1; j < liste.length; j++) {
paare++;
const dE = abstand(oklab(liste[i].echt), oklab(liste[j].echt));
if (dE < engstes.d) engstes = { d: dE, a: liste[i].name, b: liste[j].name };
if (dE < NICHT_UNTERSCHEIDBAR) ununterscheidbar++;
const ra = ausHex(liste[i].ton), rb = ausHex(liste[j].ton);
if (ra && rb) engstesRoh = Math.min(engstesRoh, abstand(oklab(ra), oklab(rb)));
}
}
const mitKontrast = liste.filter((k) => k.kontrast !== null);
const zuDunkel = mitKontrast.filter((k) => k.kontrast < 4.5);
return {
moeglich: true,
anzahl: liste.length,
/* Wie viele Kacheltexte nicht lesbar sind -- und welche. */
textGemessen: mitKontrast.length,
textZuWenig: zuDunkel.length,
textSchlechteste: mitKontrast.length
? mitKontrast.reduce((a, b) => (a.kontrast < b.kontrast ? a : b))
: null,
textNamen: zuDunkel.slice(0, 5).map((k) => `${k.name} (${k.kontrast.toFixed(2)}:1)`),
liste,
engstes,
engstesRoh,
ununterscheidbar,
paare,
/* Wie viel von der Rohfarbe auf dem Bildschirm ankommt. Faellt
das unter ein Drittel, nuetzt die schoenste Rohfarbe nichts. */
anteil: engstesRoh > 0 ? (engstes.d / engstesRoh) * 100 : 0,
};
} catch (f) {
return { moeglich: false, grund: f?.message || String(f) };
} finally {
try { if (browser) await browser.close(); } catch { /* Zugabe */ }
try { if (vorbau) await new Promise((r) => vorbau.close(r)); } catch { /* Zugabe */ }
try { if (kind) kind.kill(); } catch { /* Zugabe */ }
try { rmSync(ordner, { recursive: true, force: true }); } catch { /* Zugabe */ }
}
}
+83 -4
View File
@@ -57,10 +57,14 @@ const MINDEST_BUNTHEIT = 0.12;
/* Was festgelegt ist — mit dem Grund, aus dem es festgelegt wurde. */
const FESTGELEGT = [
{ ton: 28, kachel: "Vertraulich melden",
warum: "dieselbe Farbe wie fuer Regeln & Hilfe -- wer Hilfe sucht, landet bei einem von beiden" },
{ ton: 38, kachel: "Draußen", hex: "#ff1a1a",
warum: "knalliges Rot, traegt ab 20 Uhr den LIVE-Punkt" },
/* BERICHTIGT AM 22.09.2026. Ich hatte screen5 und screen6 vertauscht
und das Rot auf "Draussen" gelegt. Filipe: "nicht die kachel
draussen soll rot sein sondern die kachel vertraulich melden nur
die soll knall rot sein!!!" */
{ ton: 38, kachel: "Vertraulich melden", hex: "#ff1a1a",
warum: "knalliges Rot -- wer den vertraulichen Weg sucht, soll ihn finden, ohne zu lesen" },
{ ton: 39, kachel: "Draußen",
warum: "die Farbe, die Regeln & Hilfe hatte -- ausdruecklicher Wunsch (screen5)" },
];
let ok = 0, fehl = 0;
@@ -225,5 +229,80 @@ console.log("\n=== Gegenprobe ===");
pruef("ein blasser Ton wuerde auffallen (#ffe5ae)", buntheit("#ffe5ae") < MINDEST_BUNTHEIT);
}
/* =====================================================================
5. WAS AUF DEM BILDSCHIRM ANKOMMT -- der Teil, der gefehlt hat
=====================================================================
Alles bisher misst `--ton`: die ROHFARBE aus start.css. Und genau
damit war diese Pruefung gruen, waehrend Filipe am 22.09.2026 sagte:
"ich seh immer wieder sehr viele die einfach komplett die gleiche
farbe haben."
Er hatte recht. Die Kachel zeichnet ihren Ton nicht als Flaeche,
sondern als Verlauf unter einem Sternenfeld, einer Vignette und
(bis heute) einer fest blauen Wolke. Gemessen an echten
Bildpunkten: Von 0,0978 Abstand kamen 0,0141 an -- 14,4 Prozent.
ACHT Paare waren fuer ein Auge nicht mehr zu unterscheiden.
Eine Pruefung, die die Rohfarbe misst, beantwortet die Frage
"sehen die Kacheln verschieden aus" NICHT. Sie beantwortet
"stehen verschiedene Zahlen in der Datei". Das ist derselbe Fehler
wie am 01.09.2026 beim `every()` auf einem leeren Feld: Sie lief,
sie war gruen, und sie war wertlos.
Deshalb hier der Browser. Er kostet zwei Minuten -- und er ist der
einzige, der die Frage beantworten kann.
===================================================================== */
if (process.env.OHNE_BROWSER !== "ja") {
console.log("\n=== Und was davon auf dem Bildschirm ankommt ===");
const { pruefeEchteFarben } = await import("./helfer-kachel-echtfarbe.mjs");
const e = await pruefeEchteFarben();
if (!e.moeglich) {
/* DER DRITTE AUSGANG. Kein Browser, kein openssl, kein Port --
das ist "konnte nicht nachsehen", nicht "in Ordnung". */
console.log(` KONNTE NICHT NACHSEHEN: ${e.grund}`);
console.log(" (mit OHNE_BROWSER=ja laeuft die Pruefung bewusst ohne diesen Teil)");
fehl++;
} else {
pruef(`${e.anzahl} Kacheln am echten Bildschirm gemessen`, e.anzahl >= 20,
`nur ${e.anzahl} gefunden`);
/* DIE ZAHL GEHOERT IN DIE BEDINGUNG -- sonst waere dieser ganze
Abschnitt bei null Kacheln trivial gruen. */
pruef("kein Paar sieht auf dem Bildschirm gleich aus "
+ `(engstes: ${e.engstes.d.toFixed(4)}, ${e.engstes.a} <-> ${e.engstes.b})`,
e.anzahl >= 20 && e.ununterscheidbar === 0,
`${e.ununterscheidbar} von ${e.paare} Paaren unter 0,02`);
/* WIE VIEL VON DER ROHFARBE UEBERHAUPT ANKOMMT. Faellt das wieder
unter ein Drittel, ist die Gestaltung der Kachel der Grund --
und dann nuetzt die schoenste Rohfarbe nichts. */
pruef(`mindestens ein Drittel der Rohfarbe kommt an (${e.anteil.toFixed(1)} %)`,
e.anteil >= 33,
`nur ${e.anteil.toFixed(1)} % -- die Kachelgestaltung schluckt die Farbe`);
/* UND DER TEXT MUSS DARAUF LESBAR BLEIBEN.
================================================================
Diese zwei Zeilen sind die wichtigsten des ganzen Abschnitts.
Am 22.09.2026 wurde die Farbe kraeftiger gemacht -- richtig, und
gemessen. Dabei fielen 27 von 31 Kacheltexten unter 4,5:1;
vorher war es EINER. Kein Messwert hat das gemeldet, weil keine
Pruefung danach gefragt hat: `pruef-buehne` nimmt Stichproben
und traf die Kacheltexte nicht.
Farbe und Lesbarkeit gehoeren in DIESELBE Messung. Wer sie
trennt, verbessert das eine und verliert das andere -- und
merkt es erst, wenn jemand sagt, er koenne nichts mehr lesen. */
pruef(`${e.textGemessen} Kacheltexte auf Lesbarkeit gemessen`,
e.textGemessen >= 20, `nur ${e.textGemessen}`);
pruef("jeder Kacheltext erreicht 4,5:1 auf seiner Kachel"
+ (e.textSchlechteste
? ` (schlechtester: ${e.textSchlechteste.name} ${e.textSchlechteste.kontrast.toFixed(2)}:1)`
: ""),
e.textGemessen >= 20 && e.textZuWenig === 0,
`${e.textZuWenig} zu dunkel: ${e.textNamen.join(", ")}`);
}
}
console.log(`\n ${ok} bestanden, ${fehl} fehlgeschlagen\n`);
process.exit(fehl ? 1 : 0);
+26 -14
View File
@@ -1299,8 +1299,14 @@ const TREFF_BEREICHE = [
besser als der erste Vorschlag), Kontrast 6,08:1. */
{ ...TREFF_GRUPPE, name: "Material", unter: "Bilder und Videos zum Posten – jedes nur einmal",
zeichen: "galerie", ton: 42, ziel: "material.html", szene: "studio" },
/* DIE FARBE, DIE "REGELN & HILFE" HATTE (22.09.2026).
Filipes screen5: "die farbe dieser kachel soll die farbe von der
kachel: regeln & hilfe werden". Regeln & Hilfe bekommt dafuer
eine eigene -- zwei Kacheln mit derselben Farbe in derselben
Ansicht waeren genau das, was screen4 abschafft.
Ton 39 ist GESETZT. */
{ ...TREFF_GRUPPE, name: "Draußen", unter: "Ist er live? Kanäle, Shop & dein Rabatt",
zeichen: "hinaus", ton: 38, ziel: "unsere-seiten.html", szene: "portal" },
zeichen: "hinaus", ton: 39, ziel: "unsere-seiten.html", szene: "portal" },
{ ...TREFF_GRUPPE, name: "Mitmachen", unter: "Wie du dazugeh\u00f6ren kannst",
zeichen: "personen", ton: 32, ziel: "bereich.html?b=mitmachen", szene: "garage" },
/* UNSERE SEITEN (19.09.2026) -- die einzige Kachel, die HINAUSFUEHRT.
@@ -1343,11 +1349,11 @@ const TREFF_BEREICHE = [
Das Brett gibt es weiter; treff-regeln.html verlinkt darauf. Ein
Ort fuer die Frage, nicht zwei. */
/* EIGENER TON SEIT 22.09.2026. Ton 28 traegt jetzt "Vertraulich
melden" -- ausdruecklicher Wunsch, dort gesetzt. Zwei Kacheln
mit derselben Farbe in derselben Ansicht waeren genau das, was
screen4 abschaffen soll. */
zeichen: "schutz", ton: 39, ziel: "treff-regeln.html", szene: "studio" },
/* EIGENER TON (22.09.2026). "Draussen" traegt jetzt die Farbe,
die hier stand -- ausdruecklicher Wunsch. Zwei Kacheln mit
derselben Farbe in derselben Ansicht waeren genau das, was
screen4 abschafft. */
zeichen: "schutz", ton: 28, ziel: "treff-regeln.html", szene: "studio" },
];
/* MELDUNGEN & MASSNAHMEN -- FUER DAS TEAM, NICHT FUER DIE COMMUNITY
@@ -1466,15 +1472,21 @@ export const TREFF_ROLLEN = new Set(["gast", "modi", "hand", "linke", "admin"]);
const HILFE_KACHEL = {
gruppe: "Für dich", gruppeUnter: "Deine eigene Seite",
name: "Vertraulich melden", unter: "Nur DogFather und die rechte Hand",
/* DIESELBE FARBE WIE "REGELN & HILFE" -- AUSDRUECKLICH SO GEWOLLT
(22.09.2026, Filipe: "die farbe dieser kachel soll die farbe von
der kachel: regeln & hilfe werden ... und diese zwei kacheln
sollen egal wie dan auch so bleiben wie hier erwartet").
/* KNALLROT -- AUSDRUECKLICH DIESE KACHEL (berichtigt 22.09.2026).
==================================================================
Filipe: "nicht die kachel draussen soll rot sein sondern die
kachel vertraulich melden nur die soll knall rot sein!!!"
Beide gehoeren zusammen: Wer Hilfe sucht, landet bei einem von
beiden. Ton 28 ist damit GESETZT -- die Farbverteilung von
screen4 fasst ihn nicht an, sie weicht ihm aus. */
zeichen: "schutz", ton: 28, ziel: "hilfe.html", szene: "studio",
Ich hatte screen5 und screen6 vertauscht und das Rot auf
"Draussen" gelegt. Hier sitzt es richtig: Ein vertraulicher
Meldeweg ist das, was jemand im schlimmsten Moment sucht -- und
dann zaehlt, dass man ihn auf der Wand sofort findet, ohne zu
lesen. Rot ist hier die Aussage, nicht die Dekoration.
#ff1a1a, voll gesaettigt, Kontrast 4,84:1 gegen den Grund.
Ton 38 ist GESETZT: pruef-kachelfarben wird rot, wenn ein
Farblauf ihn anfasst. */
zeichen: "schutz", ton: 38, ziel: "hilfe.html", szene: "studio",
};
/* DAS TEAM SIEHT DEN TREFF MIT -- als eigene Gruppe unter seinen