CRYONOVA Schritt 3: Feldfokus, Blickfaenge, Testkorrekturen
Der Feldfokus zeigt jetzt den Ion-Rand und den weichen Schein, den das
Design-System auf Seite 15 verlangt. Er fehlte, weil der allgemeine
Tastaturring seinen dunklen Innenring darueberlegte: Dessen Selektor hat
Spezifitaet 0,3,0, die Feldregel nur 0,2,1 -- und Spezifitaet schlaegt
Reihenfolge, egal wie weit unten die Regel steht. Eingabefelder sind
jetzt vom Innenring ausgenommen; sie brauchen ihn nicht, weil sie selbst
eine dunkle Flaeche sind. Der Aqua-Umriss bleibt fuer alle erhalten.
Die zwei Blickfang-Motive stehen jetzt auch wirklich auf einer Seite:
das Wappen auf "Ueber mich", direkt nach dem Absatz ueber die Herkunft
des Namens, der Monolith auf "Portfolio" zwischen Einleitung und den
echten Projekten. Beide als Zierbild ausgezeichnet -- ihre Aussage steht
schon im Text daneben. Auf dem Handy laedt automatisch die kleine
Fassung.
Vier Fehler steckten in der Pruefung selbst, nicht im Stilblatt:
- Sie griff das erste "input" der Anfrageseite. Das sind aber sechs
optisch versteckte Auswahl-Radios, kein Textfeld.
- Sie mass ohne Fensterfokus. Ein Browser wendet :focus nur an, wenn das
Fenster selbst den Fokus hat -- das DOM meldet trotzdem brav
matches(":focus") === true, die Farbe bleibt die alte.
- Sie mass mitten im Uebergang, bevor die Animation stand.
- Und sie zaehlte Schattenebenen an "),", einem Muster, das in keinem
Schattenwert je vorkommt. Geprueft wird jetzt die Anforderung selbst:
Ion-Farbe und ein Weichzeichner ueber 0.
Geprueft: 334 Pruefungen gruen (CRYONOVA 53, Blickfang 13, Abmelden 16,
Angebot 54, Abbruch 42, System 5, Automatik 55, Angebote 56, Kette 40).
Co-Authored-By: Claude Opus 5 <[email protected]>
@@ -2556,7 +2556,20 @@ a.wd-karte:active, .wd-karte--klickbar:active {
|
|||||||
.wd :is(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
|
.wd :is(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
|
||||||
outline: 2px solid rgb(var(--wd-aqua-rgb));
|
outline: 2px solid rgb(var(--wd-aqua-rgb));
|
||||||
outline-offset: 2px;
|
outline-offset: 2px;
|
||||||
/* Der dunkle Innenring trennt das Aqua von hellen Flaechen. */
|
}
|
||||||
|
|
||||||
|
/* Der dunkle Innenring trennt das Aqua von hellen Flaechen.
|
||||||
|
|
||||||
|
Eingabefelder sind hier BEWUSST ausgenommen: Sie sind selbst eine
|
||||||
|
dunkle, vertiefte Flaeche, das Aqua hebt sich davon ohnehin ab. Ein
|
||||||
|
Innenring waere nicht nur ueberfluessig, er ueberschreibt auch den
|
||||||
|
Ion-Schein, den das System auf Seite 15 fuer den Feldfokus verlangt.
|
||||||
|
|
||||||
|
Warum die Ausnahme im Selektor stehen muss und nicht einfach weiter
|
||||||
|
unten ueberschrieben wird: Diese Regel hat Spezifitaet 0,3,0
|
||||||
|
(Klasse + :is + :focus-visible), die Feldregel nur 0,2,1 -- und
|
||||||
|
Spezifitaet schlaegt Reihenfolge, egal wie weit unten sie steht. */
|
||||||
|
.wd :is(a, button, summary, [tabindex]):focus-visible {
|
||||||
box-shadow: 0 0 0 4px rgb(3 8 16 / .85);
|
box-shadow: 0 0 0 4px rgb(3 8 16 / .85);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2641,3 +2654,188 @@ a.wd-karte:active, .wd-karte--klickbar:active {
|
|||||||
.wd-status--kritisch { background: rgb(var(--wd-fehler-rgb) / .13); color: var(--wd-fehler); border-color: rgb(var(--wd-fehler-rgb) / .35); }
|
.wd-status--kritisch { background: rgb(var(--wd-fehler-rgb) / .13); color: var(--wd-fehler); border-color: rgb(var(--wd-fehler-rgb) / .35); }
|
||||||
.wd-status--pausiert { background: rgb(var(--wd-chrome-rgb) / .12); color: var(--wd-chrome); border-color: rgb(var(--wd-chrome-rgb) / .30); }
|
.wd-status--pausiert { background: rgb(var(--wd-chrome-rgb) / .12); color: var(--wd-chrome); border-color: rgb(var(--wd-chrome-rgb) / .30); }
|
||||||
.wd-status--vip { background: rgb(var(--wd-gold-rgb) / .13); color: var(--wd-gold); border-color: rgb(var(--wd-gold-rgb) / .35); }
|
.wd-status--vip { background: rgb(var(--wd-gold-rgb) / .13); color: var(--wd-gold); border-color: rgb(var(--wd-gold-rgb) / .35); }
|
||||||
|
|
||||||
|
/* =====================================================================
|
||||||
|
SIGNATURE-VERLAEUFE (Design-System Seite 18)
|
||||||
|
|
||||||
|
Vier Verlaeufe mit je einer klaren Aufgabe. Das System gibt dazu eine
|
||||||
|
Dosierungsregel, die wichtiger ist als die Verlaeufe selbst:
|
||||||
|
|
||||||
|
"Maximal ein Signature-Verlauf pro sichtbarer Bildschirmhoehe.
|
||||||
|
Keine Regenbogenflaechen. Kein Vollflaechen-Neon.
|
||||||
|
Champagne niemals mit Violet mischen."
|
||||||
|
|
||||||
|
Das letzte Verbot ist keine Geschmacksfrage: Champagne steht fuer
|
||||||
|
Treue und Bezahltes, Violet fuer Spezielles. Mischt man beides, kann
|
||||||
|
niemand mehr ablesen, was eine Flaeche bedeutet -- und genau die
|
||||||
|
Ablesbarkeit ist der Zweck eines Farbsystems.
|
||||||
|
|
||||||
|
Deshalb sind es HILFSKLASSEN und keine Vorgabe auf Kacheln: Ein
|
||||||
|
Verlauf, der automatisch auf jeder Karte laege, waere sofort eine
|
||||||
|
Regenbogenflaeche. Man setzt ihn bewusst an eine Stelle.
|
||||||
|
===================================================================== */
|
||||||
|
|
||||||
|
/* CRYO FLOW -- primaere Energie. Baby zu Ion zu Aqua. */
|
||||||
|
.wd-verlauf--cryo {
|
||||||
|
background: linear-gradient(135deg,
|
||||||
|
rgb(var(--wd-blau-rgb) / .95) 0%,
|
||||||
|
rgb(var(--wd-ion-rgb) / .90) 50%,
|
||||||
|
rgb(var(--wd-aqua-rgb) / .85) 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* PRISM SHIFT -- Premium und Fokus. Ion zu Indigo. */
|
||||||
|
.wd-verlauf--prisma {
|
||||||
|
background: linear-gradient(135deg,
|
||||||
|
rgb(var(--wd-ion-rgb) / .92) 0%,
|
||||||
|
rgb(var(--wd-indigo-rgb) / .92) 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* AURORA CUT -- Spezialmoment. Indigo zu Violet. */
|
||||||
|
.wd-verlauf--aurora {
|
||||||
|
background: linear-gradient(135deg,
|
||||||
|
rgb(var(--wd-indigo-rgb) / .90) 0%,
|
||||||
|
rgb(var(--wd-lila-rgb) / .90) 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* LIQUID VALUE -- VIP und Treue. Champagne allein, mit einer dunklen
|
||||||
|
Tiefe statt einer zweiten Akzentfarbe: Das System verbietet die
|
||||||
|
Mischung mit Violet ausdruecklich. */
|
||||||
|
.wd-verlauf--liquid {
|
||||||
|
background: linear-gradient(135deg,
|
||||||
|
rgb(var(--wd-gold-rgb) / .92) 0%,
|
||||||
|
rgb(var(--wd-gold-rgb) / .55) 55%,
|
||||||
|
rgb(var(--wd-dunkelblau-2)) 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Text auf einem Signature-Verlauf braucht immer dunkle Schrift --
|
||||||
|
alle vier sind hell genug, dass helle Schrift darauf verschwindet. */
|
||||||
|
.wd-verlauf--cryo, .wd-verlauf--prisma,
|
||||||
|
.wd-verlauf--aurora, .wd-verlauf--liquid { color: var(--wd-schwarz); }
|
||||||
|
|
||||||
|
/* =====================================================================
|
||||||
|
FORMULARE UND TABELLEN (Design-System Seite 15)
|
||||||
|
|
||||||
|
"Fokus: Eingabefeld erhaelt Ion-Blue-Rand und sanften Glow.
|
||||||
|
Tabellenzeile erhaelt beim Hover ein sehr schwaches,
|
||||||
|
positionsabhaengiges Licht."
|
||||||
|
|
||||||
|
Das Wort "sehr schwach" ist hier die eigentliche Anweisung. Eine
|
||||||
|
Tabelle ist zum Lesen da: Wandert beim Ueberfahren ein kraeftiges
|
||||||
|
Licht mit, springt der Blick staendig zur Maus statt zur Zahl, die
|
||||||
|
man eigentlich sucht. Deshalb liegt die Deckkraft hier bei einem
|
||||||
|
Bruchteil dessen, was eine Kachel bekommt.
|
||||||
|
===================================================================== */
|
||||||
|
|
||||||
|
/* Der Fokus im Eingabefeld: Ion-Rand plus weicher Schein.
|
||||||
|
Die Vertiefung bleibt erhalten -- sonst springt das Feld beim
|
||||||
|
Anklicken optisch aus der Flaeche heraus. */
|
||||||
|
.wd-feld input:focus, .wd-feld select:focus, .wd-feld textarea:focus,
|
||||||
|
.wd-eingabe:focus {
|
||||||
|
border-color: rgb(var(--wd-ion-rgb) / .85);
|
||||||
|
box-shadow:
|
||||||
|
var(--wd-tiefe),
|
||||||
|
0 0 0 4px rgb(var(--wd-ion-rgb) / .16),
|
||||||
|
0 0 18px -4px rgb(var(--wd-ion-rgb) / .45);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ein Feld mit Fehler traegt IMMER auch Text -- die Farbe allein
|
||||||
|
waere fuer niemanden lesbar, der sie nicht unterscheiden kann. */
|
||||||
|
.wd-feld--fehler input, .wd-feld--fehler select, .wd-feld--fehler textarea {
|
||||||
|
border-color: rgb(var(--wd-fehler-rgb) / .7);
|
||||||
|
}
|
||||||
|
.wd-feld--fehler input:focus, .wd-feld--fehler select:focus,
|
||||||
|
.wd-feld--fehler textarea:focus {
|
||||||
|
box-shadow:
|
||||||
|
var(--wd-tiefe),
|
||||||
|
0 0 0 4px rgb(var(--wd-fehler-rgb) / .18);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Tabellen ---------- */
|
||||||
|
.wd-tabelle {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-size: .92rem;
|
||||||
|
}
|
||||||
|
.wd-tabelle th {
|
||||||
|
text-align: left;
|
||||||
|
font-size: .74rem;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: .06em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--wd-text-soft);
|
||||||
|
padding: .6rem .8rem;
|
||||||
|
border-bottom: 1px solid rgb(var(--wd-blau-rgb) / .22);
|
||||||
|
}
|
||||||
|
.wd-tabelle td {
|
||||||
|
padding: .7rem .8rem;
|
||||||
|
border-bottom: 1px solid rgb(255 255 255 / .05);
|
||||||
|
color: var(--wd-text-mid);
|
||||||
|
}
|
||||||
|
/* Zahlen in gleicher Ziffernbreite: Sonst stehen Betraege in einer
|
||||||
|
Spalte nicht untereinander, und man vergleicht sie falsch. */
|
||||||
|
.wd-tabelle td.wd-zahl, .wd-tabelle th.wd-zahl {
|
||||||
|
text-align: right;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (hover: hover) and (pointer: fine) {
|
||||||
|
.wd-tabelle tbody tr {
|
||||||
|
--wd-lichtx: 50%;
|
||||||
|
position: relative;
|
||||||
|
transition: background-color .2s ease;
|
||||||
|
}
|
||||||
|
/* Das positionsabhaengige Licht der Zeile -- bewusst nur 7 %, ein
|
||||||
|
Viertel dessen, was eine Kachel bekommt. Es soll die Zeile
|
||||||
|
nachzeichnen, nicht den Blick fangen. */
|
||||||
|
.wd-tabelle tbody tr:hover {
|
||||||
|
background: linear-gradient(90deg,
|
||||||
|
transparent 0%,
|
||||||
|
rgb(var(--wd-blau-rgb) / .07) var(--wd-lichtx),
|
||||||
|
transparent 100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.wd-tabelle tbody tr:hover { background: rgb(var(--wd-blau-rgb) / .05); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =====================================================================
|
||||||
|
KACHEL-ANATOMIE (Design-System Seite 11)
|
||||||
|
|
||||||
|
Das System beschreibt sechs Bestandteile: Material (Obsidian Glass
|
||||||
|
statt flacher Farbe), Kante (1 px Baby Blue, 24-36 % Deckkraft),
|
||||||
|
Lumen, Inhalt ueber dem Licht, Tiefe (zarter Schatten plus innere
|
||||||
|
Lichtkante) und Zustand.
|
||||||
|
|
||||||
|
Fuenf davon standen bereits. Was fehlte, ist die INNERE LICHTKANTE:
|
||||||
|
eine haarfeine helle Linie an der Oberkante. Sie ist der Unterschied
|
||||||
|
zwischen "dunkles Rechteck" und "Objekt aus Glas" -- Licht faellt von
|
||||||
|
oben, also faengt die obere Kante es ein. Ohne sie wirkt jede Kachel
|
||||||
|
flach, egal wie gut die Farben sind.
|
||||||
|
===================================================================== */
|
||||||
|
.wd-karte {
|
||||||
|
/* BEIDE Hintergrundebenen, nicht nur eine.
|
||||||
|
|
||||||
|
Die Kachel baut ihre Kante ueber einen zweiten Verlauf auf der
|
||||||
|
border-box -- der Rahmen selbst ist durchsichtig. Ein erster
|
||||||
|
Versuch setzte hier nur die Flaeche (padding-box) und liess die
|
||||||
|
Randebene damit ersatzlos wegfallen: Der Rand mass danach
|
||||||
|
rgba(0,0,0,0), also gar nichts. Sichtbar war das kaum, messbar
|
||||||
|
sofort -- und das System verlangt ausdruecklich eine Kante von
|
||||||
|
1 px Baby Blue mit 24-36 % Deckkraft (Seite 11). */
|
||||||
|
background:
|
||||||
|
linear-gradient(180deg,
|
||||||
|
rgb(var(--wd-glas-rgb) / .58),
|
||||||
|
rgb(6 19 35 / .72)) padding-box,
|
||||||
|
linear-gradient(140deg,
|
||||||
|
rgb(var(--wd-blau-rgb) / .36),
|
||||||
|
rgb(var(--wd-blau-rgb) / .10) 45%,
|
||||||
|
rgb(var(--wd-lila-rgb) / .26)) border-box;
|
||||||
|
/* Die innere Lichtkante: eine haarfeine helle Linie oben. Sie ist
|
||||||
|
der Unterschied zwischen "dunkles Rechteck" und "Objekt aus
|
||||||
|
Glas" -- Licht faellt von oben, also faengt die obere Kante es
|
||||||
|
ein. Ohne sie wirkt jede Kachel flach, egal wie gut die Farben
|
||||||
|
sind. */
|
||||||
|
box-shadow:
|
||||||
|
var(--wd-hoehe-1),
|
||||||
|
inset 0 1px 0 rgb(var(--wd-blau-rgb) / .16);
|
||||||
|
}
|
||||||
|
|||||||
|
After Width: | Height: | Size: 304 KiB |
|
After Width: | Height: | Size: 960 KiB |
|
After Width: | Height: | Size: 298 KiB |
|
After Width: | Height: | Size: 914 KiB |
|
Before Width: | Height: | Size: 133 KiB After Width: | Height: | Size: 161 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 53 KiB |
@@ -0,0 +1,76 @@
|
|||||||
|
/* Prueft, dass die beiden Blickfang-Motive wirklich ankommen: richtige
|
||||||
|
Datei je Geraet, sichtbare Groesse, und keine 404 auf dem Weg. */
|
||||||
|
import { chromium } from "playwright";
|
||||||
|
import http from "http";
|
||||||
|
import fs from "fs";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
const TYP = {
|
||||||
|
".html": "text/html", ".css": "text/css", ".js": "text/javascript",
|
||||||
|
".png": "image/png", ".svg": "image/svg+xml", ".webp": "image/webp",
|
||||||
|
".json": "application/json", ".ico": "image/x-icon", ".jpg": "image/jpeg",
|
||||||
|
};
|
||||||
|
|
||||||
|
const fehlt = [];
|
||||||
|
const srv = http.createServer((q, r) => {
|
||||||
|
const u = decodeURIComponent(q.url.split("?")[0]);
|
||||||
|
const p = path.join(process.cwd(), u);
|
||||||
|
if (fs.existsSync(p) && !p.endsWith("/")) {
|
||||||
|
r.writeHead(200, { "content-type": TYP[path.extname(p)] || "text/plain" });
|
||||||
|
return r.end(fs.readFileSync(p));
|
||||||
|
}
|
||||||
|
fehlt.push(u);
|
||||||
|
r.writeHead(404);
|
||||||
|
r.end("");
|
||||||
|
});
|
||||||
|
|
||||||
|
let ok = 0, fehl = 0;
|
||||||
|
const pruefe = (name, gut, info) => {
|
||||||
|
if (gut) { ok++; console.log(" ok " + name + (info ? " -> " + info : "")); }
|
||||||
|
else { fehl++; console.log(" FEHL " + name + (info ? " -> " + info : "")); }
|
||||||
|
};
|
||||||
|
|
||||||
|
srv.listen(4402, async () => {
|
||||||
|
const browser = await chromium.launch();
|
||||||
|
|
||||||
|
for (const [seite, kl] of [["ueber", "wappen"], ["portfolio", "monolith"]]) {
|
||||||
|
for (const [gr, vp] of [["pc", { width: 1440, height: 950 }],
|
||||||
|
["handy", { width: 390, height: 844 }]]) {
|
||||||
|
const s = await browser.newPage({ viewport: vp });
|
||||||
|
await s.goto(`http://127.0.0.1:4402/webdesign/${seite}.html`);
|
||||||
|
await s.waitForTimeout(1400);
|
||||||
|
|
||||||
|
const el = s.locator(".wd-blickfang--" + kl);
|
||||||
|
const r = await el.evaluate((e) => {
|
||||||
|
const c = getComputedStyle(e);
|
||||||
|
const b = e.getBoundingClientRect();
|
||||||
|
const treffer = c.backgroundImage.match(/img\/([a-z0-9-]+\.webp)/i);
|
||||||
|
return {
|
||||||
|
datei: treffer ? treffer[1] : "(keins)",
|
||||||
|
h: Math.round(b.height), w: Math.round(b.width),
|
||||||
|
versteckt: e.getAttribute("aria-hidden"),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Auf dem Handy MUSS die kleine Fassung kommen -- sonst laedt ein
|
||||||
|
Telefon ueber Mobilfunk die volle Datei fuer nichts. */
|
||||||
|
const richtig = gr === "handy" ? r.datei.includes("-klein") : !r.datei.includes("-klein");
|
||||||
|
pruefe(`${seite}/${gr}: richtige Bilddatei`, richtig, r.datei);
|
||||||
|
pruefe(`${seite}/${gr}: sichtbar gross`, r.h > 80 && r.w > 200, `${r.w}x${r.h}`);
|
||||||
|
pruefe(`${seite}/${gr}: als Zierbild ausgezeichnet`, r.versteckt === "true", r.versteckt);
|
||||||
|
|
||||||
|
await el.scrollIntoViewIfNeeded();
|
||||||
|
await s.waitForTimeout(700);
|
||||||
|
await s.screenshot({ path: `blickfang-${seite}-${gr}.png` });
|
||||||
|
await s.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const verpasst = fehlt.filter((f) => /\.(webp|png|css|js|svg)$/.test(f));
|
||||||
|
pruefe("keine fehlenden Dateien", verpasst.length === 0, verpasst.join(", ") || "keine");
|
||||||
|
|
||||||
|
console.log(`\n===== ${ok} bestanden, ${fehl} fehlgeschlagen =====`);
|
||||||
|
await browser.close();
|
||||||
|
srv.close();
|
||||||
|
process.exit(fehl ? 1 : 0);
|
||||||
|
});
|
||||||
@@ -77,6 +77,14 @@ const hexZuRgb = (h) => [1, 3, 5].map((i) => parseInt(h.substr(i, 2), 16));
|
|||||||
|
|
||||||
const browser = await chromium.launch();
|
const browser = await chromium.launch();
|
||||||
const seite = await browser.newPage({ viewport: { width: 1440, height: 950 } });
|
const seite = await browser.newPage({ viewport: { width: 1440, height: 950 } });
|
||||||
|
|
||||||
|
/* Ohne das misst jede :focus-Pruefung den Ruhezustand. Ein Browser
|
||||||
|
wendet :focus nur an, wenn das FENSTER selbst den Fokus hat -- im
|
||||||
|
Testlauf hat es den nie. Das DOM meldet trotzdem brav
|
||||||
|
element.matches(":focus") === true, die Farbe bleibt aber die alte.
|
||||||
|
Genau diese Kombination hat hier zwei Fehlalarme erzeugt. */
|
||||||
|
const cdp = await seite.context().newCDPSession(seite);
|
||||||
|
await cdp.send("Emulation.setFocusEmulationEnabled", { enabled: true });
|
||||||
await seite.goto("http://127.0.0.1:4372/webdesign/index.html");
|
await seite.goto("http://127.0.0.1:4372/webdesign/index.html");
|
||||||
await seite.waitForTimeout(500);
|
await seite.waitForTimeout(500);
|
||||||
|
|
||||||
@@ -399,6 +407,116 @@ for (const b of bilder) {
|
|||||||
pruefe("alle Motive sind vorhanden und unter 300 KB",
|
pruefe("alle Motive sind vorhanden und unter 300 KB",
|
||||||
zuGross.length === 0, zuGross.join(", ") || "alle schlank");
|
zuGross.length === 0, zuGross.join(", ") || "alle schlank");
|
||||||
|
|
||||||
|
console.log("\n=== 12. SIGNATURE-VERLAEUFE (Seite 18) ===");
|
||||||
|
await seite.goto("http://127.0.0.1:4372/webdesign/index.html");
|
||||||
|
await seite.waitForTimeout(400);
|
||||||
|
|
||||||
|
const verlaeufe = await seite.evaluate(() => {
|
||||||
|
const alle = Array.from(document.styleSheets)
|
||||||
|
.flatMap((b) => { try { return Array.from(b.cssRules); } catch { return []; } });
|
||||||
|
const text = alle.map((r) => r.cssText).join("\n");
|
||||||
|
const gefunden = ["cryo", "prisma", "aurora", "liquid"]
|
||||||
|
.filter((n) => text.includes(".wd-verlauf--" + n));
|
||||||
|
|
||||||
|
/* Champagne NIEMALS mit Violet mischen (Seite 18). Das ist keine
|
||||||
|
Geschmacksfrage: Champagne steht fuer Treue und Bezahltes, Violet
|
||||||
|
fuer Spezielles. Gemischt kann niemand mehr ablesen, was eine
|
||||||
|
Flaeche bedeutet -- und Ablesbarkeit ist der Zweck des Systems. */
|
||||||
|
const liquid = alle.find((r) => (r.selectorText || "").includes("wd-verlauf--liquid"));
|
||||||
|
const liquidText = liquid ? liquid.cssText : "";
|
||||||
|
return {
|
||||||
|
gefunden,
|
||||||
|
champagneRein: !/--wd-lila-rgb|--wd-indigo-rgb/.test(liquidText),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
pruefe("alle vier Signature-Verlaeufe sind gebaut",
|
||||||
|
verlaeufe.gefunden.length === 4, verlaeufe.gefunden.join(", "));
|
||||||
|
pruefe("Liquid Value mischt Champagne NICHT mit Violet",
|
||||||
|
verlaeufe.champagneRein, "Verbot aus Seite 18 eingehalten");
|
||||||
|
|
||||||
|
console.log("\n=== 13. FORMULARE UND TABELLEN (Seite 15) ===");
|
||||||
|
await seite.goto("http://127.0.0.1:4372/webdesign/anfrage.html");
|
||||||
|
await seite.waitForTimeout(600);
|
||||||
|
|
||||||
|
/* Echter Klick statt evaluate-focus(), und danach warten: Der Rand hat
|
||||||
|
einen Uebergang. Sofort gemessen sieht man die Animation auf halbem
|
||||||
|
Weg -- Rand noch alt, Schein noch bei 0 0 0 0 und durchsichtig.
|
||||||
|
|
||||||
|
Der Selektor nennt ausdruecklich Texteingaben: Die Anfrageseite
|
||||||
|
beginnt mit sechs Auswahlkacheln, deren Radios optisch versteckt
|
||||||
|
sind. Ein blindes "erstes input" greift die statt des Feldes. */
|
||||||
|
await seite.locator('.wd-feld textarea, .wd-feld input[type="text"], .wd-eingabe')
|
||||||
|
.first().click();
|
||||||
|
await seite.waitForTimeout(700);
|
||||||
|
|
||||||
|
const feldFokus = await seite.evaluate(() => {
|
||||||
|
/* Nur ECHTE Texteingaben. Die Anfrageseite beginnt mit sechs
|
||||||
|
Auswahlkacheln, deren Radios optisch versteckt sind -- ein blindes
|
||||||
|
"erstes input" greift die und misst deren neutrales Weiss statt des
|
||||||
|
Ion-Randes. Genau das hat hier zweimal falschen Alarm ausgeloest. */
|
||||||
|
const f = document.activeElement;
|
||||||
|
if (!f || f === document.body) return null;
|
||||||
|
const st = getComputedStyle(f);
|
||||||
|
return { rand: st.borderColor, schatten: st.boxShadow };
|
||||||
|
});
|
||||||
|
/* Das System nennt ausdruecklich "Ion-Blue-Rand und sanften Glow". */
|
||||||
|
pruefe("das Eingabefeld bekommt im Fokus einen Ion-Rand",
|
||||||
|
feldFokus && /74,\s*200,\s*255/.test(feldFokus.rand), feldFokus && feldFokus.rand);
|
||||||
|
/* Geprueft wird die Anforderung selbst, nicht die Anzahl Kommas:
|
||||||
|
Der Schein muss in Ion-Blue leuchten und WEICH sein, also einen
|
||||||
|
Weichzeichner ueber 0 haben. Eine harte 0px-Kante waere ein Rahmen,
|
||||||
|
kein Schein. (Die frueher hier stehende Zaehlung trennte an "),"
|
||||||
|
-- das Muster kommt in keinem Schattenwert je vor, weil nach der
|
||||||
|
Farbe immer noch die Masse folgen.) */
|
||||||
|
const scheinIon = feldFokus && /74,\s*200,\s*255/.test(feldFokus.schatten);
|
||||||
|
const scheinWeich = feldFokus &&
|
||||||
|
[...feldFokus.schatten.matchAll(/74,\s*200,\s*255[^)]*\)((?:\s+-?\d+px){2,4})/g)]
|
||||||
|
.some((t) => parseFloat(t[1].trim().split(/\s+/)[2]) > 0);
|
||||||
|
pruefe("und einen weichen Schein dazu",
|
||||||
|
scheinIon && scheinWeich,
|
||||||
|
feldFokus && feldFokus.schatten.slice(0, 60));
|
||||||
|
|
||||||
|
const tabelle = await seite.evaluate(() => {
|
||||||
|
const alle = Array.from(document.styleSheets)
|
||||||
|
.flatMap((b) => { try { return Array.from(b.cssRules); } catch { return []; } })
|
||||||
|
.map((r) => r.cssText).join("\n");
|
||||||
|
/* "sehr schwaches, positionsabhaengiges Licht" -- die Betonung liegt
|
||||||
|
auf SCHWACH. Eine Tabelle ist zum Lesen da; ein kraeftiges
|
||||||
|
Wanderlicht zieht den Blick zur Maus statt zur Zahl. */
|
||||||
|
const m = alle.match(/wd-tabelle tbody tr:hover[^}]*rgb\(var\(--wd-blau-rgb\)\s*\/\s*\.(\d+)\)/);
|
||||||
|
return { gebaut: alle.includes(".wd-tabelle"), staerke: m ? Number("0." + m[1]) : null,
|
||||||
|
positionsabhaengig: /wd-tabelle tbody tr:hover[^}]*--wd-lichtx/.test(alle) };
|
||||||
|
});
|
||||||
|
pruefe("Tabellen sind gestaltet", tabelle.gebaut);
|
||||||
|
pruefe("das Zeilenlicht ist positionsabhaengig", tabelle.positionsabhaengig);
|
||||||
|
pruefe("und bleibt sehr schwach (unter 10 %)",
|
||||||
|
tabelle.staerke !== null && tabelle.staerke < 0.10,
|
||||||
|
tabelle.staerke !== null ? Math.round(tabelle.staerke * 100) + " %" : "nicht gefunden");
|
||||||
|
|
||||||
|
console.log("\n=== 14. KACHEL-ANATOMIE (Seite 11) ===");
|
||||||
|
await seite.goto("http://127.0.0.1:4372/webdesign/leistungen.html");
|
||||||
|
await seite.waitForTimeout(600);
|
||||||
|
const anatomie = await seite.evaluate(() => {
|
||||||
|
const k = document.querySelector(".wd-karte");
|
||||||
|
if (!k) return null;
|
||||||
|
const st = getComputedStyle(k);
|
||||||
|
return {
|
||||||
|
ebenen: (st.backgroundImage.match(/gradient/g) || []).length,
|
||||||
|
lichtkante: /inset/.test(st.boxShadow),
|
||||||
|
randbreite: st.borderWidth,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
/* Die Kachel baut ihre Kante ueber eine ZWEITE Hintergrundebene auf der
|
||||||
|
border-box -- der Rahmen selbst ist durchsichtig. Wer beim Anpassen
|
||||||
|
nur die Flaeche setzt, laesst die Kante ersatzlos wegfallen: Genau
|
||||||
|
das ist beim Bauen passiert, der Rand mass danach rgba(0,0,0,0).
|
||||||
|
Sichtbar war es kaum, messbar sofort. */
|
||||||
|
pruefe("die Kachel hat zwei Hintergrundebenen (Flaeche UND Kante)",
|
||||||
|
anatomie && anatomie.ebenen >= 2, anatomie && anatomie.ebenen);
|
||||||
|
pruefe("sie hat eine innere Lichtkante", anatomie && anatomie.lichtkante);
|
||||||
|
pruefe("die Kante ist 1 px stark (System Seite 11)",
|
||||||
|
anatomie && anatomie.randbreite === "1px", anatomie && anatomie.randbreite);
|
||||||
|
|
||||||
await browser.close();
|
await browser.close();
|
||||||
srv.close();
|
srv.close();
|
||||||
const f = ergebnisse.filter((e) => !e.ok).length;
|
const f = ergebnisse.filter((e) => !e.ok).length;
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 161 KiB After Width: | Height: | Size: 160 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 803 KiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 456 KiB After Width: | Height: | Size: 466 KiB |
|
Before Width: | Height: | Size: 368 KiB After Width: | Height: | Size: 614 KiB |
|
Before Width: | Height: | Size: 215 KiB After Width: | Height: | Size: 232 KiB |
|
Before Width: | Height: | Size: 347 KiB After Width: | Height: | Size: 332 KiB |
|
Before Width: | Height: | Size: 137 KiB After Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 368 KiB After Width: | Height: | Size: 357 KiB |
|
Before Width: | Height: | Size: 829 KiB After Width: | Height: | Size: 796 KiB |
|
Before Width: | Height: | Size: 382 KiB After Width: | Height: | Size: 346 KiB |
@@ -70,6 +70,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<!-- Blickfang: der Monolith. Steht zwischen Einleitung und den zwei
|
||||||
|
echten Projekten -- ein ruhiger Moment, bevor die Arbeit kommt.
|
||||||
|
aria-hidden, weil rein atmosphaerisch: Die Aussage der Seite
|
||||||
|
steckt in den Projekten darunter, nicht in diesem Bild. -->
|
||||||
|
<section class="wd-abschnitt wd-abschnitt--eng" style="padding-top:0">
|
||||||
|
<div class="wd-container wd-container--schmal">
|
||||||
|
<div class="wd-blickfang wd-blickfang--monolith wd-auf" aria-hidden="true"></div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section class="wd-abschnitt" style="padding-top:0">
|
<section class="wd-abschnitt" style="padding-top:0">
|
||||||
<div class="wd-container">
|
<div class="wd-container">
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
Start alle alten Zwischenspeicher weg. Muss bei jeder Änderung an den
|
Start alle alten Zwischenspeicher weg. Muss bei jeder Änderung an den
|
||||||
Dateien unten hochgezählt werden, sonst hängen Nutzer auf einem alten
|
Dateien unten hochgezählt werden, sonst hängen Nutzer auf einem alten
|
||||||
Stand fest. */
|
Stand fest. */
|
||||||
const CACHE_NAME = "dogfather-webdesign-v32";
|
const CACHE_NAME = "dogfather-webdesign-v33";
|
||||||
|
|
||||||
/* Bausteine, die die Oberfläche zum Anzeigen braucht. Bewusst KEINE
|
/* Bausteine, die die Oberfläche zum Anzeigen braucht. Bewusst KEINE
|
||||||
HTML-Datei in dieser Liste. */
|
HTML-Datei in dieser Liste. */
|
||||||
|
|||||||
@@ -68,6 +68,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<!-- ---------------- Blickfang: das Wappen ----------------
|
||||||
|
Ein Bild, das man ansieht, keine Flaeche mit Text darauf. Es
|
||||||
|
steht bewusst hier: nach dem Absatz, der erklaert, woher der
|
||||||
|
Name kommt.
|
||||||
|
|
||||||
|
aria-hidden, weil es rein atmosphaerisch ist -- alles, was es
|
||||||
|
zeigt, steht schon im Text daneben. Es vorzulesen wuerde nichts
|
||||||
|
ergaenzen, nur den Lesefluss unterbrechen. -->
|
||||||
|
<section class="wd-abschnitt wd-abschnitt--eng" style="padding-top:0">
|
||||||
|
<div class="wd-container wd-container--schmal">
|
||||||
|
<div class="wd-blickfang wd-blickfang--wappen wd-auf" aria-hidden="true"></div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<!-- ---------------- Arbeitsweise ---------------- -->
|
<!-- ---------------- Arbeitsweise ---------------- -->
|
||||||
<section class="wd-abschnitt wd-abschnitt--gehoben">
|
<section class="wd-abschnitt wd-abschnitt--gehoben">
|
||||||
<div class="wd-container">
|
<div class="wd-container">
|
||||||
|
|||||||