diff --git a/server/pruef-support-bilder.mjs b/server/pruef-support-bilder.mjs index d52bc5f5..3ad51714 100644 --- a/server/pruef-support-bilder.mjs +++ b/server/pruef-support-bilder.mjs @@ -656,6 +656,158 @@ melde("\n=== Eine Farbe je Runde ==="); await h.kontext.close(); } +/* ===================================================================== + 7. SIND DIE TEXTE IN DER KARTE ZU LESEN? (03.10.2026) + ===================================================================== + + Filipe: „nur die kacheln noch viel geiler. so dass ich die texte + auch besser erkenne." + + GEMESSEN, BEVOR ETWAS ANGEFASST WURDE -- und die naheliegende + Vermutung war falsch: + + Kontrast aller zehn Textarten 7,5 bis 15,7 : 1 + ZEICHEN JE ZEILE 147 + + Am Kontrast lag es nicht. Es lag an der Zeilenlaenge: Auf 1280 px + lief der Meldungstext ueber 1120 px, also knapp 150 Zeichen. + Angenehm sind 45 bis 75; darueber findet das Auge beim + Zeilenwechsel die naechste Zeile nicht mehr sicher. + + ZWEI ZAHLEN WERDEN DESHALB GEPRUEFT, und beide mussten vorher + gemessen werden, um sie ueberhaupt zu kennen. Eine Regel, die aus + einer Messung kommt, ist etwas anderes als eine, die man sich + ausdenkt. + ===================================================================== */ +melde("\n=== Sind die Texte zu lesen? ==="); +{ + const { kontext, seite } = await neueSeite(1280, keksModi); + await seite.locator(".s-karte__text").first().waitFor({ timeout: 8000 }); + + const lesbar = await seite.evaluate(() => { + /* ==== ZWEI SCHREIBWEISEN, EINE EINHEIT ====================== + + `rgb()` zaehlt 0..255, `color(srgb …)` zaehlt 0..1 -- und + genau das liefert Chrome fuer jedes `color-mix()`. Beim Bauen + dieser Messung hat mich das getaeuscht: 0,77 als 0,77/255 + gelesen ergibt fast Schwarz, und drei gut lesbare Texte + standen mit Kontrast 1,04 da. Fast haette ich Farben + repariert, die in Ordnung sind. */ + const zahl = (t) => { + const n = (t.match(/[\d.]+/g) || []).map(Number); + if (/^color\(/.test(t)) { + return [n[0] * 255, n[1] * 255, n[2] * 255, n.length > 3 ? n[3] : 1]; + } + return n; + }; + const kanal = (c) => { + const x = c / 255; + return x <= 0.03928 ? x / 12.92 : ((x + 0.055) / 1.055) ** 2.4; + }; + const hell = ([r, g, b]) => 0.2126 * kanal(r) + 0.7152 * kanal(g) + 0.0722 * kanal(b); + const mischen = (v, h, a) => v.map((x, i) => x * a + h[i] * (1 - a)); + /* DER GRUND WIRD NACH OBEN GESUCHT, bis eine undurchsichtige + Farbe kommt -- halbdurchsichtige Kaesten uebereinander sind + sonst nicht zu rechnen. */ + const grundVon = (e) => { + let n = e; const stapel = []; + while (n && n !== document.documentElement) { + const bg = zahl(getComputedStyle(n).backgroundColor); + const a = bg.length === 4 ? bg[3] : 1; + if (a > 0) stapel.push([bg.slice(0, 3), a]); + if (a === 1) break; + n = n.parentElement; + } + let farbe = [11, 15, 22]; + for (let i = stapel.length - 1; i >= 0; i--) { + farbe = mischen(stapel[i][0], farbe, stapel[i][1]); + } + return farbe; + }; + const kontrast = (e) => { + const vg = zahl(getComputedStyle(e).color); + const a = vg.length === 4 ? vg[3] : 1; + const grund = grundVon(e); + const vorne = mischen(vg.slice(0, 3), grund, a); + const l1 = hell(vorne), l2 = hell(grund); + const [h, d] = l1 > l2 ? [l1, l2] : [l2, l1]; + return (h + 0.05) / (d + 0.05); + }; + + const arten = [".s-karte__text", ".s-marke", ".s-karte__zeit", ".s-runde__nr", + ".s-runde__von", ".s-runde__zeit", ".s-schritt__schild", ".s-schritt__text", + ".s-rundenleiste__punkt"]; + const werte = []; + for (const w of arten) { + const e = document.querySelector(w); + if (!e) continue; + const st = getComputedStyle(e); + const px = parseFloat(st.fontSize); + werte.push({ + w, px: Math.round(px * 100) / 100, + k: Math.round(kontrast(e) * 100) / 100, + zeichen: Math.round(e.getBoundingClientRect().width / (px * 0.5)), + gross: px >= 24 || (px >= 18.66 && Number(st.fontWeight) >= 700), + }); + } + /* GEGENPROBE IM BROWSER: Ein absichtlich zu blasser Text MUSS + auffallen. Ohne sie waere „alle ueber 4,5" auch dann gruen, + wenn die Rechnung kaputt ist -- und genau das war sie. */ + const probe = document.createElement("p"); + probe.textContent = "Probe"; + probe.style.color = "#17202c"; + document.querySelector(".s-karte").append(probe); + const blass = Math.round(kontrast(probe) * 100) / 100; + probe.remove(); + + return { werte, blass }; + }); + + ok(lesbar.werte.length >= 8, + `${lesbar.werte.length} Textarten in der Karte gemessen`); + + const text = lesbar.werte.find((x) => x.w === ".s-karte__text"); + /* DER MELDUNGSTEXT IST DIE HAUPTSACHE DER SEITE. */ + ok(text && text.px >= 16, + `der Meldungstext ist ${text?.px} px gross (mindestens 16)`); + ok(text && text.zeichen <= 85, + `und bricht nach etwa ${text?.zeichen} Zeichen um (hoechstens 85, ` + + "vorher waren es 147)"); + + const verlauf = lesbar.werte.find((x) => x.w === ".s-schritt__text"); + ok(verlauf && verlauf.px >= 15 && verlauf.zeichen <= 85, + `der Verlaufstext: ${verlauf?.px} px, etwa ${verlauf?.zeichen} Zeichen`); + + /* UND KEIN TEXT UNTER DER KONTRASTSCHWELLE. */ + const schwach = lesbar.werte.filter((x) => x.k < (x.gross ? 3 : 4.5)); + ok(schwach.length === 0, + `jeder Text haelt die Kontrastschwelle (${schwach.length} darunter` + + (schwach.length ? `: ${schwach.map((x) => `${x.w} ${x.k}`).join(", ")}` : "") + + `) — schwaechster ${Math.min(...lesbar.werte.map((x) => x.k))} : 1`); + ok(lesbar.blass < 4.5, + `Gegenprobe: ein absichtlich blasser Text faellt auf (${lesbar.blass} : 1)`); + + /* UND AUF DEM HANDY: kurze Zeilen sind dort von selbst kurz -- die + Frage ist, ob die groessere Schrift die Seite sprengt. */ + await kontext.close(); + const h = await neueSeite(390, keksModi); + await h.seite.locator(".s-karte__text").first().waitFor({ timeout: 8000 }); + const handy = await h.seite.evaluate(() => { + const e = document.querySelector(".s-karte__text"); + const px = parseFloat(getComputedStyle(e).fontSize); + return { + px: Math.round(px * 100) / 100, + zeichen: Math.round(e.getBoundingClientRect().width / (px * 0.5)), + ueber: document.documentElement.scrollWidth - document.documentElement.clientWidth, + }; + }); + ok(handy.ueber <= 0, + `auf 390 px ragt mit der groesseren Schrift nichts heraus (${handy.ueber} px)`); + ok(handy.zeichen >= 25 && handy.zeichen <= 60, + `und die Zeile bleibt lesbar lang (etwa ${handy.zeichen} Zeichen)`); + await h.kontext.close(); +} + await browser.close(); try { rmSync(ordner, { recursive: true, force: true }); } catch { /* egal */ } diff --git a/workspace/anruf-probe.html b/workspace/anruf-probe.html index 40f89b83..0d0d8e79 100644 --- a/workspace/anruf-probe.html +++ b/workspace/anruf-probe.html @@ -16,8 +16,8 @@ verdeckt. Gefunden hat es pruef-struktur. Richtig sind `gate.css` (die Farbwerte des Hauses) und `haus.css`. --> - - + + - +