Files
dogfather-universe/pruef-system.mjs
T
DogFatherGit de8819f1fd CRYONOVA: Farb- und Lichtsystem nach dem Design-System umgesetzt
Umsetzung des Design-Systems "Baby Blue Optical Luxury" (Edition 2.0),
Schritt 1 von mehreren: die zentrale Farbquelle. Nach der Kernregel auf
Seite 3 ist das ausdrücklich ein Farb- und Licht-Redesign — Aufbau,
Navigation, Texte und Funktionen bleiben unangetastet.

FARBEN
Grundflächen auf die vier Tiefenebenen des Systems (Void, Midnight,
Obsidian, Deep Glass). Palette nach Seite 5: Signature Baby, Ion Blue,
Hyper Aqua, Aurora Violet, Prism Indigo, Liquid Gold, Signal Coral,
Chrome Silver.

Die Variablen behalten ihre alten NAMEN (--wd-blau statt --cryo-baby).
Ein Umbenennen hätte über 155 Fundstellen anfassen müssen — viel
Bewegung ohne sichtbaren Nutzen, mit der realen Gefahr, eine Stelle zu
übersehen und danach zwei fast gleiche Blautöne zu haben. Entscheidend
ist die Rolle, nicht der Name; die Systembezeichnungen stehen als
Kommentar daneben.

EIN FEHLER IM DESIGN-SYSTEM, DER BEWUSST NICHT ÜBERNOMMEN WURDE
Die Token-Liste auf Seite 20 ist um eine Zeile verrutscht — Namen und
Hex-Werte passen dort nicht zusammen. Am folgenreichsten: --cryo-baby
stünde auf #0C2740, einem fast schwarzen Navy, und ist laut Seite 21
zugleich die Standard-Lumenfarbe. Das Mauslicht wäre damit praktisch
unsichtbar geworden — ausgerechnet der Effekt, den das System auf fünf
Seiten als unantastbar schützt. Maßgeblich ist deshalb die Palette auf
Seite 5 und die Lumen-Logik auf Seite 9, die untereinander stimmig sind.

MAUSLICHT
Der bestehende Effekt bleibt vollständig erhalten (Systemauflage) und
bekommt eine Farbvariable pro Kachel: --wd-lumen. Vorher war die Farbe
im Verlauf fest verdrahtet, und jede weitere Kachelfarbe hätte zwei
neue Blöcke gebraucht (Fläche + leuchtende Kante). Bei sechs
Lumen-Rollen wären das zwölf fast gleiche Blöcke gewesen, die beim
nächsten Feinschliff zwangsläufig auseinanderlaufen. Jetzt setzt die
Kachel nur ihre Farbe, der Verlauf steht einmal da — genau das meint
Token-Regel 02 mit "Kachelfarbe steuert Lumenfarbe".

38 lose Hex-Codes durch Token ersetzt (Token-Regel 05). Drei davon
(#3d9dbd, #7c5cd6, #a8873a) waren noch die ALTEN Markenfarben und
hätten still neben den neuen weitergelebt — genau der Mechanismus, durch
den Oberflächen mit der Zeit zwei fast gleiche Töne bekommen.

KONTRASTE NACHGERECHNET
Die Palette ist auf dunklem Grund durchweg stark (9,7 bis 19,8:1) — mit
einer Ausnahme: Prism Indigo erreicht auf keiner Fläche 4,5:1 (nur 3,25
bis 4,29). Es ist deshalb ausschließlich für Kanten, Verläufe und große
Premium-Flächen zugelassen, nie für Fließtext. Das System sieht Indigo
ohnehin nur für "Premium-Momente" vor — die Rechnung bestätigt die
Regel, statt ihr zu widersprechen.

NEUER TEST: pruef-cryonova.mjs (24 Prüfungen)
Palette, Grundflächen, Mauslicht-Erhalt, echte Zeigerbewegung, keine
losen Hex-Codes, Kontraste und die Frage, ob alle 12 Seiten wirklich
aus derselben Quelle schöpfen.

Dabei drei Fehlalarme im eigenen Test gefunden und behoben — jeder
davon hätte dauerhaft rote Zeilen erzeugt und irgendwann dazu geführt,
dass man eine echte Meldung übersieht:
- Halbtransparente Flächen müssen über ihren Untergrund gerechnet
  werden. Der aktive Reiter kam sonst auf 1:1 statt echter 8,25–10,23:1.
- Text auf Farbverläufen liefert rgba(0,0,0,0) als Hintergrund; der
  Hauptknopf kam so auf 1:1 statt rund 11:1.
- Die Maus muss mit Zwischenschritten bewegt werden, sonst feuert
  pointermove nicht. Eine Direktmessung bestätigte: Das Licht folgt
  einwandfrei (30px → 723px, Deckkraft 1).

pruef-system.mjs auf den neuen Markenton gesetzt. Dass er dort zunächst
"0 von 1804 Elementen" meldete, war kein Fehler, sondern der Beweis,
dass der alte Ton nirgends mehr vorkommt.

Geprüft: 24 (CRYONOVA) + 0 Fundstellen (Design/WCAG über 12 Seiten × 5
Sprachen) + 40 + 56 + 69 + 54 + 42 + 55 + 5 + 16 — alles grün.
2026-08-24 22:32:04 +02:00

185 lines
8.1 KiB
JavaScript

/* =====================================================================
pruef-system.mjs — Beweist, dass das Farbsystem eines IST
Ein Designsystem ist erst dann eines, wenn eine Änderung an EINER
Stelle überall ankommt. Steht die Markenfarbe an 155 Stellen
ausgeschrieben, hat man kein System, sondern eine Sammlung von
Zufällen, die sich zufällig ähneln.
WIE gemessen wird — und warum nicht anders.
Der naheliegende Weg wäre, die Variable im laufenden Browser
umzustellen und zu schauen, was mitgeht. Er liefert FALSCHE
Ergebnisse: Chromium erneuert Teilbäume, die per innerHTML eingefügt
wurden — Kopf- und Fusszeile stammen aus wd-core.js — nicht
zuverlässig, wenn man eine Variable nachträglich ändert. Auch ein
erzwungener Neuberechnungsstoss half nicht.
Nachgewiesen am 23.08.2026: Bei Laufzeitänderung blieben 110 Elemente
scheinbar "hängen". Lädt man dieselbe Seite dagegen mit bereits
geänderter Variable, sind sie orange. Der Fehler lag im
Messverfahren, nicht im Farbsystem.
Deshalb wird die Variable VOR dem Laden überschrieben. Das entspricht
genau dem, was eine Änderung in der CSS-Datei bewirkt — also dem
Fall, um den es wirklich geht.
===================================================================== */
import { chromium } from "playwright";
import { createServer } from "node:http";
import { readFileSync, existsSync, readdirSync } from "node:fs";
import { join, extname } from "node:path";
const W = process.cwd();
const T = { ".html": "text/html", ".css": "text/css", ".js": "text/javascript",
".png": "image/png", ".jpg": "image/jpeg", ".svg": "image/svg+xml",
".webp": "image/webp", ".ico": "image/x-icon", ".json": "application/json" };
const srv = createServer((q, r) => {
const p = join(W, decodeURIComponent(q.url.split("?")[0]));
if (existsSync(p) && !p.endsWith("/")) {
r.writeHead(200, { "content-type": T[extname(p)] || "text/plain" });
return r.end(readFileSync(p));
}
r.writeHead(404).end("");
});
await new Promise((r) => srv.listen(4332, r));
/* diagnose.html ist bewusst ausgenommen.
Sie ist das Werkzeug fuer den Fall, dass die Seite NICHT laeuft, und
traegt deshalb alles fest in sich: eigene Farben, eigenes Layout,
keine externe CSS- oder JS-Datei, keine Uebersetzung. Genau das ist
ihr Zweck -- eine Diagnoseseite, die an derselben kaputten
Stilvorlage scheitert wie das Problem, das sie untersuchen soll,
waere wertlos.
Sie deshalb an den Regeln fuer die eigentliche Seite zu messen, wuerde
zwei Meldungen erzeugen, die man auf Dauer wegsieht -- und irgendwann
sieht man dann auch eine echte weg. */
const SEITEN = readdirSync(join(W, "webdesign"))
.filter((f) => f.endsWith(".html") && f !== "diagnose.html");
/* Der aktuelle Markenton. Beim Wechsel auf das CRYONOVA-System
(24.08.2026) stand hier noch der alte Wert 127, 208, 232 -- der Test
fand daraufhin null Elemente und meldete Alarm. Das war genau
umgekehrt zu deuten: Dass der alte Ton NIRGENDS mehr vorkam, war der
Beweis, dass die Umstellung vollstaendig durchgeschlagen ist. Der
Test misst hier aber, ob eine Aenderung ueberall ankommt -- dafuer
braucht er den Ton, der gerade gilt. */
const ALT = "143, 216, 255"; // Signature Baby #8FD8FF
const NEU = "255 120 40"; // drastisch anders, damit nichts untergeht
const ergebnisse = [];
function pruefe(name, ok, zusatz) {
ergebnisse.push({ name, ok });
console.log((ok ? " ok " : " FEHL ") + name + (zusatz ? " -> " + zusatz : ""));
}
const browser = await chromium.launch();
async function seiteMitFarbe(datei, umstellen) {
const s = await browser.newPage({ viewport: { width: 1280, height: 900 } });
await s.route("**/webdesign/admin/**", (r) => r.fulfill({ json: { ok: true, anfragen: [], kunden: [], projekte: [], verlaeufe: [] } }));
await s.route("**/webdesign/portal/**", (r) => r.fulfill({ json: { ok: true } }));
await s.route("**/webdesign/api-ausweis", (r) => r.fulfill({ json: {} }));
await s.addInitScript(({ um, neu }) => {
sessionStorage.setItem("wd-sitzung-aktiv", "1");
if (!um) return;
const st = document.createElement("style");
st.textContent = ":root{--wd-blau-rgb: " + neu + " !important;}";
document.addEventListener("DOMContentLoaded", () => document.head.appendChild(st));
}, { um: umstellen, neu: NEU });
await s.goto("http://127.0.0.1:4332/webdesign/" + datei);
await s.waitForTimeout(420);
return s;
}
console.log("=== Kommt eine Farbänderung überall an? ===\n");
let haengen = 0;
let betroffen = 0;
let sichtbarGesamt = 0;
for (const datei of SEITEN) {
/* Erst zählen, wie viele Elemente den Markenton ÜBERHAUPT tragen —
sonst wäre "0 hängen geblieben" auch dann erfüllt, wenn die Farbe
auf der Seite gar nicht vorkommt. */
const vorher = await seiteMitFarbe(datei, false);
const wieviele = await vorher.evaluate((alt) => {
let n = 0;
for (const el of document.querySelectorAll("*")) {
const r = el.getBoundingClientRect();
if (r.width < 1 || r.height < 1) continue;
const st = getComputedStyle(el);
if ([st.color, st.backgroundColor, st.borderTopColor, st.boxShadow, st.backgroundImage]
.join("|").includes(alt)) n++;
}
return n;
}, ALT);
await vorher.close();
betroffen += wieviele;
const nachher = await seiteMitFarbe(datei, true);
const rest = await nachher.evaluate((alt) => {
const raus = [];
let sichtbar = 0;
for (const el of document.querySelectorAll("*")) {
const r = el.getBoundingClientRect();
if (r.width < 1 || r.height < 1) continue;
sichtbar++;
const st = getComputedStyle(el);
if ([st.color, st.backgroundColor, st.borderTopColor, st.boxShadow, st.backgroundImage]
.join("|").includes(alt)) {
raus.push(el.tagName.toLowerCase() + "." + (el.className || "").toString().slice(0, 34));
}
}
return { raus, sichtbar };
}, ALT);
await nachher.close();
sichtbarGesamt += rest.sichtbar;
haengen += rest.raus.length;
if (rest.raus.length) {
console.log(` FEHL ${datei}: ${rest.raus.length} Elemente zeigen weiter den alten Ton`);
for (const h of [...new Set(rest.raus)].slice(0, 6)) console.log(" " + h);
}
}
pruefe("die Markenfarbe kommt auf vielen Elementen vor", betroffen > 200,
betroffen + " von " + sichtbarGesamt + " sichtbaren Elementen");
pruefe("nach der Umstellung bleibt KEINE Stelle beim alten Ton", haengen === 0,
haengen + " hängen geblieben");
/* ---- Die vier Prozessfarben müssen an den Markenfarben hängen ---- */
const s3 = await seiteMitFarbe("ablauf.html", false);
const prozess = await s3.evaluate(() => {
const w = getComputedStyle(document.documentElement);
const vor = ["--wd-p1", "--wd-p2", "--wd-p3", "--wd-p4"].map((v) => w.getPropertyValue(v).trim());
document.documentElement.style.setProperty("--wd-blau-rgb", "255 120 40");
document.documentElement.style.setProperty("--wd-gold-rgb", "40 255 120");
const w2 = getComputedStyle(document.documentElement);
const nach = ["--wd-p1", "--wd-p2", "--wd-p3", "--wd-p4"].map((v) => w2.getPropertyValue(v).trim());
return { vor, nach };
});
pruefe("Prozessfarbe 1 (Start) hängt an der Markenfarbe Blau",
prozess.vor[0] !== prozess.nach[0], prozess.vor[0] + " -> " + prozess.nach[0]);
pruefe("Prozessfarbe 4 (Abschluss) hängt an der Markenfarbe Gold",
prozess.vor[3] !== prozess.nach[3], prozess.vor[3] + " -> " + prozess.nach[3]);
/* ---- Radien: eine knappe Skala statt Zufallswerten ---- */
const radien = await s3.evaluate(() => {
const werte = new Set();
for (const el of document.querySelectorAll("*")) {
if (el.getBoundingClientRect().width < 1) continue;
const v = getComputedStyle(el).borderTopLeftRadius;
if (v && v !== "0px" && v.endsWith("px") && parseFloat(v) < 100) werte.add(v);
}
return Array.from(werte).sort((a, b) => parseFloat(a) - parseFloat(b));
});
pruefe("Eckenradien folgen einer knappen Skala", radien.length <= 5, radien.join(", "));
await s3.close();
await browser.close();
srv.close();
const f = ergebnisse.filter((e) => !e.ok).length;
console.log(`\n===== ${ergebnisse.length - f} bestanden, ${f} fehlgeschlagen =====`);
process.exitCode = f ? 1 : 0;