147 Zeichen je Zeile -- am Kontrast lag es nicht
Filipe: „nur die kacheln noch viel geiler. so dass ich die texte auch
besser erkenne."
NAHELIEGEND WAERE GEWESEN, die Farben aufzuhellen. Erst gemessen,
dann gebaut -- und die Vermutung war falsch:
Kontrast .s-karte__text 15,7 : 1 (noetig 4,5)
Kontrast aller zehn Textarten 7,5 bis 15,7 : 1
ZEICHEN JE ZEILE 147 (angenehm 45-75)
Am Kontrast lag es nicht; der ist ueberall ueppig. Es lag an der
ZEILENLAENGE. Auf 1280 px lief der Meldungstext ueber die ganze
Kartenbreite: 1120 px, knapp 150 Zeichen. Beim Zeilenwechsel findet
das Auge die naechste Zeile nicht mehr sicher -- man liest dieselbe
zweimal oder ueberspringt eine. Das ist der groesste Hebel beim
Lesen, und er kostet nichts.
GEAENDERT
Meldungstext 15,2 -> 17 px, max-width 72ch, Zeilenhoehe 1.6
Verlaufstext 14,4 -> 15,2 px, max-width 70ch
Kartenrand 16/18/15/22 -> 18/20/16/24 px
Trennlinie zwischen Meldung und Verlauf
147 -> 78 Zeichen je Zeile (PC), 39 auf dem Handy
`ch` UND NICHT PIXEL: `ch` ist die Breite der Null und waechst mit
der Schrift mit. Eine Angabe in Pixeln waere bei der naechsten
Schriftgroesse wieder falsch -- dieselbe Falle wie jede feste Zahl.
MEHR RAND, WEIL DIE SCHRIFT GEWACHSEN IST. Bei unveraendertem Rand
haette sie die Kante beruehrt, und die Karte waere voller gewirkt
statt lesbarer. Links bleibt es breiter: dort laeuft die
Leuchtschiene in der Farbe des Stands.
DIE TRENNLINIE kostet einen Pixel und beantwortet „wo hoert die
Meldung auf und wo faengt die Vorgeschichte an?". Vorher lief beides
ohne Zaesur ineinander.
EIN FEHLER IN MEINER EIGENEN MESSUNG -- und er haette Schaden
angerichtet
Die erste Fassung der Kontrastrechnung meldete fuer drei gut lesbare
Texte einen Kontrast von 1,04. Ich war nah dran, Farben zu
„reparieren", die in Ordnung sind.
Der Grund: `rgb()` zaehlt 0..255, `color(srgb …)` zaehlt 0..1 -- und
genau das liefert Chrome fuer jedes `color-mix()`. 0,77 als 0,77/255
gelesen ist fast Schwarz. Aufgefallen ist es nur, weil die Zahl
nicht zum Bildschirmfoto passte: Dort waren die Texte deutlich
lesbar.
DESHALB STEHT IN DER PRUEFUNG EINE GEGENPROBE IM BROWSER: Ein
absichtlich zu blasser Absatz wird eingehaengt und MUSS auffallen
(gemessen 1,23 : 1). Ohne sie waere „alle ueber 4,5" auch dann
gruen, wenn die Rechnung kaputt ist -- und genau das war sie.
GEPRUEFT -- pruef-support-bilder 56 -> 64 ok
9 Textarten in der Karte gemessen
der Meldungstext ist 16.96 px gross (mindestens 16)
und bricht nach etwa 78 Zeichen um (hoechstens 85, vorher 147)
der Verlaufstext: 15.2 px, etwa 75 Zeichen
jeder Text haelt die Kontrastschwelle (0 darunter)
— schwaechster 7.55 : 1
Gegenprobe: ein absichtlich blasser Text faellt auf (1.23 : 1)
auf 390 px ragt mit der groesseren Schrift nichts heraus (0 px)
und die Zeile bleibt lesbar lang (etwa 39 Zeichen)
Dazu gruen: pruef-css-klassen · pruef-fingermass.
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -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 */ }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user