Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
042aef9a32 | ||
|
|
a19b6cc1c9 |
No files matched your search
@@ -1,5 +1,13 @@
|
|||||||
# Qualitätsbericht – erster ausführbarer Prototyp
|
# Qualitätsbericht – erster ausführbarer Prototyp
|
||||||
|
|
||||||
|
## Ergänzung: Elektro Bernhard gemessen, 14.09.2026 (1.12.1)
|
||||||
|
|
||||||
|
Lighthouse **13** (`scripts/audit.mjs`, je drei Läufe Mobil und Desktop, Mediane) gegen den lokalen Produktionsmodus der Referenzwebsite Elektro Bernhard (`var/elektro-bernhard.db`, Port 8090), vier Adressen: `/`, `/leistungen`, `/leistungen/notdienst`, `/kontakt`. Ergebnis nach den Korrekturen: **Barrierefreiheit, Best Practices, SEO und agentisches Browsing überall 100**, Leistung überall 100 bis auf die mobile Startseite mit **99** (LCP 2,1 s durch das Hero-Foto als Hintergrund unter Mobildrosselung; CLS 0, TBT 30 ms). Berichte unter `reports/lighthouse-elektro-bernhard-2/`; der erste Lauf davor (`reports/lighthouse-elektro-bernhard/`) zeigte Barrierefreiheit 95–97.
|
||||||
|
|
||||||
|
axe (WCAG 2.0/2.1 A+AA und Best-Practice-Regeln, Playwright, 1440 und 390 px) auf sechs Seiten: **null Verstöße**. Vorher gab es drei echte Befunde, alle behoben: Markennamen `#9aa0a6` auf Weiß (2,6:1 → `#6b7178`, 4,7:1), der rote Link „Kant-IT Solutions“ im dunklen Fuß (3,0:1 → hell), und auf der Leistungsübersicht sprang die Gliederung von H1 auf H3 (jetzt eine H2 vor den Karten). Ein vierter Befund lag im CMS: der Pause-Knopf des Sliders hatte den sichtbaren Text „Pause“, aber den zugänglichen Namen „Automatische Wiedergabe pausieren“ (WCAG 2.5.3); der Name beginnt jetzt mit dem sichtbaren Text. Achtung beim Messen: unmittelbar nach dem Laden meldet axe Kontrastverstöße an Elementen, die gerade eingeblendet werden (halb transparente Zwischenzustände); erst nach dem Durchscrollen und zwei Sekunden Wartezeit sind die Befunde belastbar.
|
||||||
|
|
||||||
|
Maschinenlesbar: `llms.txt`, `robots.txt`, `sitemap.xml` werden ausgeliefert, `LocalBusiness` mit drei `sameAs`-Profilen auf der Startseite, `BreadcrumbList` auf Unterseiten, `og:image` auf allen Inhaltsseiten. Nicht gemessen: Felddaten (LCP/INP/CLS am 75. Perzentil), Screenreader, Firefox/Safari, das tatsächliche Hosting.
|
||||||
|
|
||||||
## Ergänzung: Sichtbare leere Layouts, 14.09.2026 (1.6.2)
|
## Ergänzung: Sichtbare leere Layouts, 14.09.2026 (1.6.2)
|
||||||
|
|
||||||
30 JavaScript-Tests und 27 PHP-Editor-/Sicherheitstests mit 312 Assertions bestanden. Vollständiger isolierter Chromium-Editortest einschließlich axe ohne Verstöße: direkte Tastatureinfügung in leere Sektionen/Spalten, genaues Drag-and-drop-Ziel und Scrollen am Rand, Spaltenreihenfolge auf Desktop und Mobil, Undo/Redo, Speichern/Neuladen, Veröffentlichung und Konflikte. Die Tests prüfen ausdrücklich, dass die neuen Editorhilfen weder im gespeicherten Blockbaum noch im öffentlichen HTML stehen. Desktop-, Mobil- und Drag-Screenshots visuell geprüft; Bericht unter `reports/editor/result.json`.
|
30 JavaScript-Tests und 27 PHP-Editor-/Sicherheitstests mit 312 Assertions bestanden. Vollständiger isolierter Chromium-Editortest einschließlich axe ohne Verstöße: direkte Tastatureinfügung in leere Sektionen/Spalten, genaues Drag-and-drop-Ziel und Scrollen am Rand, Spaltenreihenfolge auf Desktop und Mobil, Undo/Redo, Speichern/Neuladen, Veröffentlichung und Konflikte. Die Tests prüfen ausdrücklich, dass die neuen Editorhilfen weder im gespeicherten Blockbaum noch im öffentlichen HTML stehen. Desktop-, Mobil- und Drag-Screenshots visuell geprüft; Bericht unter `reports/editor/result.json`.
|
||||||
|
|||||||
+9
-1
@@ -1,5 +1,13 @@
|
|||||||
# Projektstatus
|
# Projektstatus
|
||||||
|
|
||||||
|
## 1.12.1 – Elektro Bernhard gemessen: Lighthouse und axe (14.09.2026)
|
||||||
|
|
||||||
|
**Anlass:** Umut fragte nach Google-Bewertung, SEO, llms.txt und Barrierefreiheit der neuen Website.
|
||||||
|
|
||||||
|
**Ergebnis:** Lighthouse-Mediane auf vier Seiten, Mobil und Desktop: alles 100, nur die mobile Startseite hat Leistung 99 (LCP 2,1 s durch das Hero-Foto). axe auf sechs Seiten in zwei Breiten: null Verstöße. Details und Messbedingungen in `docs/QUALITY.md`.
|
||||||
|
|
||||||
|
**Behoben:** drei Kontrast-/Gliederungsbefunde in der Website (Skript) und einer im CMS: der Pause-Knopf des Sliders verletzte WCAG 2.5.3 (sichtbarer Text „Pause“, Name „Automatische Wiedergabe pausieren“); `slider.js` bildet den Namen jetzt aus dem sichtbaren Text, `scripts/admin-quality-check.mjs` folgt.
|
||||||
|
|
||||||
## 1.12.0 – Soziale Profile als Baustein; Elektro Bernhard neu gestaltet (14.09.2026)
|
## 1.12.0 – Soziale Profile als Baustein; Elektro Bernhard neu gestaltet (14.09.2026)
|
||||||
|
|
||||||
**Anlass:** Umut fand den ersten Nachbau „grauenhaft“, fragte nach dem transparenten Menü und vermisste Social-Icons im CMS – „gescheit“, also einmal zentral gepflegt und überall mit eigener Größe und Farbe einsetzbar.
|
**Anlass:** Umut fand den ersten Nachbau „grauenhaft“, fragte nach dem transparenten Menü und vermisste Social-Icons im CMS – „gescheit“, also einmal zentral gepflegt und überall mit eigener Größe und Farbe einsetzbar.
|
||||||
@@ -12,7 +20,7 @@
|
|||||||
|
|
||||||
**Geprüft:** PHP-Suite 492 Tests, davon vier neue für die Profile (Darstellung, Teilmenge, Beschriftung, neuer Tab, sameAs, Abweisung falscher Werte, HTTP-Adresse); einziger Fehlschlag ist der bekannte Altfehler `testAnyPublishedPageCanBecomeTheRoot…`. 33 JavaScript-Tests, Twig-Lint, PHP-Stilprüfung (Fixer angewendet). Editor-Browserprüfung vollständig erfolgreich einschließlich axe. In der App-Vorschau: Startseite von oben bis unten, Kopf transparent und nach Scrollen weiß, Leistungsseite, Einstellungen mit den elf Profilfeldern, Fußbereich im Editor mit geöffnetem Eigenschaftenfeld des Bausteins (Größe, Form, Anordnung, Beschriftung, Tab, Netzwerke, Sichtbarkeit); Mobilansicht 375 px mit Hamburger, Kopfleiste einzeilig. Alle Adressen 200, `/gibtsnicht` 404, `sameAs` mit drei Profilen.
|
**Geprüft:** PHP-Suite 492 Tests, davon vier neue für die Profile (Darstellung, Teilmenge, Beschriftung, neuer Tab, sameAs, Abweisung falscher Werte, HTTP-Adresse); einziger Fehlschlag ist der bekannte Altfehler `testAnyPublishedPageCanBecomeTheRoot…`. 33 JavaScript-Tests, Twig-Lint, PHP-Stilprüfung (Fixer angewendet). Editor-Browserprüfung vollständig erfolgreich einschließlich axe. In der App-Vorschau: Startseite von oben bis unten, Kopf transparent und nach Scrollen weiß, Leistungsseite, Einstellungen mit den elf Profilfeldern, Fußbereich im Editor mit geöffnetem Eigenschaftenfeld des Bausteins (Größe, Form, Anordnung, Beschriftung, Tab, Netzwerke, Sichtbarkeit); Mobilansicht 375 px mit Hamburger, Kopfleiste einzeilig. Alle Adressen 200, `/gibtsnicht` 404, `sameAs` mit drei Profilen.
|
||||||
|
|
||||||
**Achtung, parallele Sitzung:** Während dieser Arbeit hat eine andere Sitzung 1.11.3 und 1.11.4 veröffentlicht und `VERSION` zurückgesetzt; hier steht wieder 1.12.0, weil ein neuer Baustein und ein neues Schema kein Patch sind. Nichts von dieser Arbeit ist committet.
|
**Veröffentlicht:** Commit `fd4cb75` und Tag `v1.12.0` auf `origin`; [Release 1.12.0](https://git.kant-it.de/kantarci/KantSites/releases/tag/v1.12.0) mit ZIP (3546 Dateien, 4,6 MB), SHA-256-Datei und `install.php`. Das Archiv wurde aus einem sauberen Worktree des Tags gebaut (`var/release-source-1.12.0`), die drei Anhänge anonym von der Releaseadresse geladen und bytegenau mit den gebauten Dateien verglichen; SHA-256 `5cc745f6…d5195e`. Während dieser Arbeit hatte eine parallele Sitzung 1.11.3 und 1.11.4 veröffentlicht und `VERSION` zurückgesetzt; 1.12.0 folgt darauf, weil ein neuer Baustein und ein neues Schema kein Patch sind.
|
||||||
|
|
||||||
## Tagespflege Moldovan – getrennte Installation vorbereitet (14.09.2026, Nachbau offen)
|
## Tagespflege Moldovan – getrennte Installation vorbereitet (14.09.2026, Nachbau offen)
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,8 @@ export function initSliders(doc = document) {
|
|||||||
dots.forEach((dot, i) => { dot.hidden = !visible.includes(i); dot.setAttribute('aria-pressed', String(i === index)); });
|
dots.forEach((dot, i) => { dot.hidden = !visible.includes(i); dot.setAttribute('aria-pressed', String(i === index)); });
|
||||||
if (play) {
|
if (play) {
|
||||||
play.textContent = playing ? 'Pause' : 'Wiedergabe'; play.dataset.playing = String(playing);
|
play.textContent = playing ? 'Pause' : 'Wiedergabe'; play.dataset.playing = String(playing);
|
||||||
play.setAttribute('aria-label', playing ? 'Automatische Wiedergabe pausieren' : 'Automatische Wiedergabe starten');
|
// Der zugängliche Name beginnt mit dem sichtbaren Text (WCAG 2.5.3): wer „Pause“ liest, kann „Pause“ sagen.
|
||||||
|
play.setAttribute('aria-label', playing ? 'Pause: automatische Wiedergabe anhalten' : 'Wiedergabe: automatisch weiterblättern');
|
||||||
play.disabled = reduced(); play.title = reduced() ? 'Automatik ist bei reduzierter Bewegung deaktiviert.' : '';
|
play.disabled = reduced(); play.title = reduced() ? 'Automatik ist bei reduzierter Bewegung deaktiviert.' : '';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -72,11 +72,11 @@ try {
|
|||||||
await page.mouse.move(1, 1); await page.waitForTimeout(7200);
|
await page.mouse.move(1, 1); await page.waitForTimeout(7200);
|
||||||
console.log('Paused slider:', JSON.stringify(await geometry()), await controls.innerText());
|
console.log('Paused slider:', JSON.stringify(await geometry()), await controls.innerText());
|
||||||
assert.ok(Math.abs((await geometry()).left - manual.left) < 2, 'manual navigation pauses autoplay');
|
assert.ok(Math.abs((await geometry()).left - manual.left) < 2, 'manual navigation pauses autoplay');
|
||||||
await controls.getByRole('button', { name: 'Automatische Wiedergabe starten' }).click();
|
await controls.getByRole('button', { name: 'Wiedergabe: automatisch weiterblättern' }).click();
|
||||||
await page.mouse.move(1, 1); await page.waitForTimeout(7700);
|
await page.mouse.move(1, 1); await page.waitForTimeout(7700);
|
||||||
assert.ok(Math.abs((await geometry()).left - manual.left) > 20, 'play resumes after manual operation');
|
assert.ok(Math.abs((await geometry()).left - manual.left) > 20, 'play resumes after manual operation');
|
||||||
await page.emulateMedia({ reducedMotion: 'reduce' });
|
await page.emulateMedia({ reducedMotion: 'reduce' });
|
||||||
assert.equal(await controls.getByRole('button', { name: 'Automatische Wiedergabe starten' }).isDisabled(), true);
|
assert.equal(await controls.getByRole('button', { name: 'Wiedergabe: automatisch weiterblättern' }).isDisabled(), true);
|
||||||
const reduced = await geometry(); await page.waitForTimeout(7100);
|
const reduced = await geometry(); await page.waitForTimeout(7100);
|
||||||
assert.ok(Math.abs((await geometry()).left - reduced.left) < 2, 'reduced motion stops autoplay');
|
assert.ok(Math.abs((await geometry()).left - reduced.left) < 2, 'reduced motion stops autoplay');
|
||||||
await controls.getByRole('button', { name: 'Vorherige Folie', exact: true }).click();
|
await controls.getByRole('button', { name: 'Vorherige Folie', exact: true }).click();
|
||||||
|
|||||||
@@ -382,7 +382,7 @@ $fuss = [
|
|||||||
$column('fuss-schluss-links', [$text('fuss-copyright', '© 2026 Elektro Bernhard GmbH', ['desktop' => ['color' => '#8f959c', 'fontSize' => '13px', 'lineHeight' => 1.8]])]),
|
$column('fuss-schluss-links', [$text('fuss-copyright', '© 2026 Elektro Bernhard GmbH', ['desktop' => ['color' => '#8f959c', 'fontSize' => '13px', 'lineHeight' => 1.8]])]),
|
||||||
$column('fuss-schluss-rechts', [
|
$column('fuss-schluss-rechts', [
|
||||||
['id' => 'fuss-rechtliches', 'type' => 'legal', 'props' => ['label' => 'Rechtliches'], 'style' => ['desktop' => ['color' => '#b3b8bf', 'fontSize' => '13px', 'justifyContent' => 'flex-end', 'padding' => ['right' => '72px']], 'mobile' => ['justifyContent' => 'flex-start', 'padding' => ['right' => '0px']]]],
|
['id' => 'fuss-rechtliches', 'type' => 'legal', 'props' => ['label' => 'Rechtliches'], 'style' => ['desktop' => ['color' => '#b3b8bf', 'fontSize' => '13px', 'justifyContent' => 'flex-end', 'padding' => ['right' => '72px']], 'mobile' => ['justifyContent' => 'flex-start', 'padding' => ['right' => '0px']]]],
|
||||||
$text('fuss-urheber', 'Website von <a href="https://www.kant-it.de/">Kant-IT Solutions</a>', ['desktop' => ['color' => '#8f959c', 'fontSize' => '13px', 'textAlign' => 'right', 'margin' => ['top' => '6px'], 'padding' => ['right' => '72px']], 'mobile' => ['textAlign' => 'left', 'padding' => ['right' => '0px']]]),
|
[...$text('fuss-urheber', 'Website von <a href="https://www.kant-it.de/">Kant-IT Solutions</a>', ['desktop' => ['color' => '#8f959c', 'fontSize' => '13px', 'textAlign' => 'right', 'margin' => ['top' => '6px'], 'padding' => ['right' => '72px']], 'mobile' => ['textAlign' => 'left', 'padding' => ['right' => '0px']]]), 'css' => '& a{color:#e8eaed}'],
|
||||||
]),
|
]),
|
||||||
], ['desktop' => ['columns' => 'two', 'gap' => 'medium'], 'mobile' => ['columns' => 'one']], ['desktop' => ['margin' => ['top' => '0px']]]),
|
], ['desktop' => ['columns' => 'two', 'gap' => 'medium'], 'mobile' => ['columns' => 'one']], ['desktop' => ['margin' => ['top' => '0px']]]),
|
||||||
], ['desktop' => ['backgroundColor' => '@nacht', 'color' => '#b3b8bf', 'padding' => ['top' => '72px', 'bottom' => '24px']], 'mobile' => ['padding' => ['top' => '52px', 'bottom' => '24px']]]),
|
], ['desktop' => ['backgroundColor' => '@nacht', 'color' => '#b3b8bf', 'padding' => ['top' => '72px', 'bottom' => '24px']], 'mobile' => ['padding' => ['top' => '52px', 'bottom' => '24px']]]),
|
||||||
@@ -632,7 +632,7 @@ $startseite = [
|
|||||||
[$column('marken-label-spalte', [$text('marken-label', 'Wir arbeiten mit bewährten Marken', ['desktop' => ['color' => '@gedaempft', 'fontSize' => '13px', 'fontWeight' => '700', 'letterSpacing' => '0.12em', 'textTransform' => 'uppercase']])])],
|
[$column('marken-label-spalte', [$text('marken-label', 'Wir arbeiten mit bewährten Marken', ['desktop' => ['color' => '@gedaempft', 'fontSize' => '13px', 'fontWeight' => '700', 'letterSpacing' => '0.12em', 'textTransform' => 'uppercase']])])],
|
||||||
array_map(static fn (string $name, int $i): array => [
|
array_map(static fn (string $name, int $i): array => [
|
||||||
'id' => 'marke-'.($i + 1), 'type' => 'column',
|
'id' => 'marke-'.($i + 1), 'type' => 'column',
|
||||||
'children' => [['id' => 'marke-name-'.($i + 1), 'type' => 'text', 'props' => ['text' => $name], 'style' => ['desktop' => ['fontSize' => '24px', 'fontWeight' => '900', 'letterSpacing' => '-0.04em', 'color' => '#9aa0a6', 'textAlign' => 'center'], 'mobile' => ['fontSize' => '20px']]]],
|
'children' => [['id' => 'marke-name-'.($i + 1), 'type' => 'text', 'props' => ['text' => $name], 'style' => ['desktop' => ['fontSize' => '24px', 'fontWeight' => '900', 'letterSpacing' => '-0.04em', 'color' => '#6b7178', 'textAlign' => 'center'], 'mobile' => ['fontSize' => '20px']]]],
|
||||||
], $marken, array_keys($marken)),
|
], $marken, array_keys($marken)),
|
||||||
), [], ['desktop' => ['display' => 'flex', 'flexWrap' => 'wrap', 'alignItems' => 'center', 'justifyContent' => 'space-between', 'gap' => '40px', 'margin' => ['top' => '0px']], 'mobile' => ['gap' => '22px', 'justifyContent' => 'center']]),
|
), [], ['desktop' => ['display' => 'flex', 'flexWrap' => 'wrap', 'alignItems' => 'center', 'justifyContent' => 'space-between', 'gap' => '40px', 'margin' => ['top' => '0px']], 'mobile' => ['gap' => '22px', 'justifyContent' => 'center']]),
|
||||||
], ['desktop' => ['padding' => ['top' => '40px', 'bottom' => '40px'], 'borderWidth' => ['top' => '1px', 'right' => '0px', 'bottom' => '1px', 'left' => '0px'], 'borderStyle' => 'solid', 'borderColor' => '@linie'], 'mobile' => ['padding' => ['top' => '32px', 'bottom' => '32px']]]),
|
], ['desktop' => ['padding' => ['top' => '40px', 'bottom' => '40px'], 'borderWidth' => ['top' => '1px', 'right' => '0px', 'bottom' => '1px', 'left' => '0px'], 'borderStyle' => 'solid', 'borderColor' => '@linie'], 'mobile' => ['padding' => ['top' => '32px', 'bottom' => '32px']]]),
|
||||||
@@ -713,6 +713,7 @@ $leistungenId = $anlegen('leistungen', null, [
|
|||||||
'blocks' => [
|
'blocks' => [
|
||||||
$seitenkopf('leistungen-seite', 'Unsere Leistungen', 'Elektrotechnik aus einer Hand', 'Wir übernehmen Beratung, Planung und Installation. Auch nach Abschluss der Arbeiten erreichen Sie uns bei Fragen oder Störungen.'),
|
$seitenkopf('leistungen-seite', 'Unsere Leistungen', 'Elektrotechnik aus einer Hand', 'Wir übernehmen Beratung, Planung und Installation. Auch nach Abschluss der Arbeiten erreichen Sie uns bei Fragen oder Störungen.'),
|
||||||
$section('leistungen-seite-raster-abschnitt', [
|
$section('leistungen-seite-raster-abschnitt', [
|
||||||
|
$fx($heading('leistungen-seite-h2', 'Vier Leistungen, ein Ansprechpartner', 2, ['desktop' => [...$h2Stil['desktop'], 'margin' => ['bottom' => '36px']], 'mobile' => $h2Stil['mobile']]), 'rise'),
|
||||||
$row('leistungen-seite-raster', array_map(
|
$row('leistungen-seite-raster', array_map(
|
||||||
static fn (array $l, int $i): array => [...$karte('leistungen-seite-'.$l[0], $l[1], $l[2], $l[3], '/leistungen/'.$l[0]), 'layout' => ['effects' => ['shadow' => 'soft', 'hover' => 'lift', 'entrance' => 'rise', 'delay' => ['none', 'short', 'medium', 'long'][$i]]]],
|
static fn (array $l, int $i): array => [...$karte('leistungen-seite-'.$l[0], $l[1], $l[2], $l[3], '/leistungen/'.$l[0]), 'layout' => ['effects' => ['shadow' => 'soft', 'hover' => 'lift', 'entrance' => 'rise', 'delay' => ['none', 'short', 'medium', 'long'][$i]]]],
|
||||||
$leistungen, array_keys($leistungen),
|
$leistungen, array_keys($leistungen),
|
||||||
|
|||||||
Reference in new issue
Block a user