Eigener Durchgang vor dem Livegang: drei Fehler, zwei Feinheiten
CMS quality / php (8.2) (push) Canceled after 0s
CMS quality / php (8.4) (push) Canceled after 0s
CMS quality / browser (push) Canceled after 0s
CMS quality / mysql (push) Canceled after 0s

Umut: "schau selbst drueber, wuerdest du so online gehen?" Nein, so nicht.

Verborgener Text stand sichtbar auf der Seite: .visually-hidden gibt es in
admin.css, aber nicht in site.css. "Bewertung: 5 von 5" neben den Sternen
und "(Pflichtfeld)" hinter jedem Formularfeld waren zu lesen - seit dem
ersten Formular. axe meldet so etwas nicht, es prueft das Ansagen, nicht
das faelschliche Sichtbarsein.

Jede Spalte war ein Kasten: Rahmen, Ecken, 30px Innenabstand von
vornherein. Eine Spalte ist ein Layoutmittel; die Karte ist jetzt eine
Einstellung. Die Demoseite wirkt dadurch ruhiger.

Der Knopf im Aufruf-Band war Tinte auf Tinte, also auf dunklem Grund fast
unsichtbar - ausgerechnet beim Handlungsaufruf. Aufruf-Band und Formular
nehmen die Akzentfarbe.

Dazu: gestapelte Karten klebten aneinander (18px), und die linke Spalte im
Kontaktbereich war halb leer.

Geprueft: 393 Tests unter PHP 8.2 und 8.4, Editortest, Browserpruefung,
axe ohne Verstoss.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
kantarciandClaude Opus 5 committed 2026-09-13 09:45:21 +02:00
1 parent 2715d329ef
commit b2c4f45474
3 files changed
+53 -5

No files matched your search

+16
View File
@@ -440,6 +440,22 @@ Vom Verweis bleibt nur der Host. Die vollständige Adresse, aus der jemand kam,
**Aufbewahrung:** nach 60 Tagen werden die Stunden eines Tages zu einer Zeile verschmolzen, nach 425 Tagen wird gelöscht. Gut ein Jahr, damit ein Vergleich mit dem Vorjahresmonat möglich bleibt. `cms:stats:prune` macht beides und ist für einen Cronjob gedacht.
## ADR 046 – Der eigene Blick: was ich vor einem Livegang ändern würde (13.09.2026)
Umut: „schau selbst drüber, ohne zu vergleichen – würdest du so online gehen?“ Antwort: **so nicht.** Fünf Dinge sind mir aufgefallen, und drei davon waren Fehler im CMS, nicht im Inhalt.
**1. Verborgener Text stand sichtbar auf der Seite.** `.visually-hidden` gibt es in `admin.css`, aber **nicht in `site.css`** – auf jeder öffentlichen Seite waren damit die Zusätze zu lesen, die nur für Vorlesesoftware gedacht sind: „Bewertung: 5 von 5“ neben den Sternen, „(Pflichtfeld)“ hinter jedem Formularfeld. Das ist der peinlichste Fund der Reihe, weil er seit dem ersten Formular dasteht und in keinem Test auffiel: axe prüft, ob etwas **angesagt** wird, nicht ob es fälschlich **sichtbar** ist.
**2. Jede Spalte war ein Kasten.** `.block-column` brachte Rahmen, Ecken und 30 px Innenabstand mit. Eine Spalte ist aber ein Layoutmittel – wer damit zwei Textblöcke nebeneinander stellt, bekam zwei umrandete Kästchen und fragt sich, warum seine Seite aussieht wie ein Formular. Die Karte ist jetzt das, was sie sein sollte: eine Einstellung, kein Standard. Die Demoseite sieht dadurch ruhiger aus, nicht ärmer.
**3. Der Knopf im Aufruf-Band war unsichtbar.** Er nahm die Tintenfarbe – auf einem dunklen Band also Tinte auf Tinte, ausgerechnet beim Handlungsaufruf. Aufruf-Band und Formular nehmen jetzt die Akzentfarbe.
**4. Gestapelte Karten klebten aneinander.** In einer Zeile sorgt das Raster für Abstand, untereinander gab es keinen. Aufeinanderfolgende Karten bekommen 18 px.
**5. Und mein eigener Inhalt:** die linke Spalte im Kontaktbereich war zur Hälfte leer, weil sie sich auf die Höhe des Formulars zog. Sie steht jetzt oben und trägt drei Karten, die die Frage beantworten, die man an dieser Stelle hat: was passiert, wenn ich abschicke?
**Was ich bewusst gelassen habe:** die Einwilligung liegt weiterhin als Kasten mitten auf der Seite. Das ist richtig so – sie ist eine Entscheidung, keine Verzierung –, aber sie verdeckt auf jedem Bildschirmfoto den Inhalt dahinter. Wer die Seite abnimmt, sollte sie einmal wegklicken, bevor er urteilt.
## ADR 045 – Der Zeigereffekt, und warum Feinschliff kein Zusatz ist (13.09.2026)
Umut: „es gibt keinen Mausover-Effekt wenn man auf einen Beitrag geht, das fehlt … grob siehts ähnlich aus, aber im Detail fehlen viele Kleinigkeiten.“ Das ist die richtige Beobachtung: eine Karte, die sich beim Zeigen nicht rührt, wirkt kaputt, und zwar bevor jemand sagen kann, warum.
+5 -5
View File
@@ -4,10 +4,10 @@
h1,h2,h3,h4,h5,h6,p,a{overflow-wrap:break-word}
h1,h2,h3,h4,h5,h6{hyphens:manual}
p{hyphens:auto}
.block-image{margin:0;max-width:100%}.block-image img{max-width:100%;height:auto}
.visually-hidden{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}.block-image{margin:0;max-width:100%}.block-image img{max-width:100%;height:auto}
:root{color-scheme:light;--paper:#f7f8f4;--ink:#182d2b;--muted:#50625d;--line:#d7dfd7;--mint:#e5f0b6;--accent:#315b49;font-family:Arial,Helvetica,sans-serif;color:var(--ink);background:var(--paper);font-synthesis:none}
*{box-sizing:border-box}body{margin:0}a{color:inherit;text-underline-offset:5px}a:hover{text-decoration-thickness:2px}:focus-visible{outline:3px solid var(--accent);outline-offset:6px}main:focus{outline:none}.shell{width:min(var(--k-container,1180px),calc(100% - 64px));margin-inline:auto}.site-header{border-bottom:1px solid var(--line)}.site-header.is-sticky{position:sticky;top:0;z-index:40;background:var(--paper);box-shadow:0 1px 0 var(--line),0 10px 30px color-mix(in srgb,var(--ink) 8%,transparent)}.image-link{display:block;text-decoration:none}.brand{display:inline-flex;align-items:center;gap:5px;font-weight:800;font-size:28px;letter-spacing:-1.3px;text-decoration:none}.brand-icon{width:37px;height:37px;background:var(--ink);color:var(--mint);border-radius:10px;display:grid;place-items:center;margin-right:7px;font-size:27px;letter-spacing:-2px}.brand-light{font-weight:400}.site-header nav{display:flex;gap:32px;align-items:center;font-size:14px;font-weight:600}.site-header nav a{text-decoration:none;padding:12px 0}.site-header nav [aria-current=page]{text-decoration:underline}.site-header nav .nav-link{border:1px solid var(--ink);padding:13px 18px;border-radius:7px}.nav-link span{margin-left:18px}.block-section{padding:70px 0}.block-section:first-child{padding:74px 0 82px}.section-inner>.block-text:first-child{font-size:12px;font-weight:700;letter-spacing:2px;text-transform:uppercase;color:color-mix(in srgb,var(--accent) 68%,currentColor);margin:0 0 30px}h1,h2,h3{letter-spacing:-.05em;line-height:1.05;margin:0 0 24px}h1{font-size:clamp(48px,6.6vw,84px);font-weight:500;max-width:900px}h2{font-size:clamp(32px,4vw,49px);font-weight:500;max-width:760px}h3{font-size:26px;letter-spacing:-.035em;line-height:1.2}.block-text{font-size:18px;line-height:1.7;max-width:690px;margin:0 0 23px;color:color-mix(in srgb,currentColor 82%,transparent);white-space:pre-line}.button{display:inline-flex;align-items:center;gap:32px;min-height:50px;margin-top:9px;padding:15px 21px;background:var(--ink);color:var(--paper);border-radius:7px;text-decoration:none;font-size:14px;font-weight:600}.button:hover{background:color-mix(in srgb,var(--ink) 88%,var(--paper))}.block-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(250px,100%),1fr));gap:22px;margin-top:38px;background:transparent!important}.block-column{border:1px solid var(--line);border-radius:12px;padding:30px;min-width:0}.block-column .block-text{font-size:15px}.block-column>.block-text:first-child{font-size:12px;font-weight:700;letter-spacing:1px;color:color-mix(in srgb,var(--accent) 68%,currentColor);margin-bottom:34px}.tone-mint{background:var(--mint)}.tone-ink{background:var(--ink);color:var(--paper)}.tone-ink .block-text{color:color-mix(in srgb,var(--paper) 86%,var(--ink))}.tone-ink .section-inner>.block-text:first-child{color:var(--mint)}.tone-ink .button{color:var(--ink);background:var(--mint)}.width-narrow>.section-inner{max-width:800px}.site-footer{border-top:1px solid var(--line)}.site-footer p,.site-footer>span{font-size:12px;color:var(--muted)}.site-footer .brand{font-size:22px}.skip-link{position:fixed;left:16px;top:-100px;z-index:10;background:var(--ink);color:white;padding:16px}.skip-link:focus{top:16px}img{display:block;max-width:100%;height:auto;border-radius:12px}.error-page{padding-block:100px}.error-page h1{max-width:800px}
@media(max-width:650px){.shell{width:calc(100% - 36px)}.site-header nav{width:100%;justify-content:space-between;gap:12px;font-size:12px}.site-header nav .nav-link{padding:10px}.nav-link span{margin-left:6px}.block-section,.block-section:first-child{padding-block:48px}h1{font-size:49px}.block-text{font-size:16px}.block-column{padding:26px}.site-footer{align-items:flex-start;flex-direction:column;gap:10px}.site-footer p{margin:0}.block-row{gap:16px}}
*{box-sizing:border-box}body{margin:0}a{color:inherit;text-underline-offset:5px}a:hover{text-decoration-thickness:2px}:focus-visible{outline:3px solid var(--accent);outline-offset:6px}main:focus{outline:none}.shell{width:min(var(--k-container,1180px),calc(100% - 64px));margin-inline:auto}.site-header{border-bottom:1px solid var(--line)}.site-header.is-sticky{position:sticky;top:0;z-index:40;background:var(--paper);box-shadow:0 1px 0 var(--line),0 10px 30px color-mix(in srgb,var(--ink) 8%,transparent)}.image-link{display:block;text-decoration:none}.brand{display:inline-flex;align-items:center;gap:5px;font-weight:800;font-size:28px;letter-spacing:-1.3px;text-decoration:none}.brand-icon{width:37px;height:37px;background:var(--ink);color:var(--mint);border-radius:10px;display:grid;place-items:center;margin-right:7px;font-size:27px;letter-spacing:-2px}.brand-light{font-weight:400}.site-header nav{display:flex;gap:32px;align-items:center;font-size:14px;font-weight:600}.site-header nav a{text-decoration:none;padding:12px 0}.site-header nav [aria-current=page]{text-decoration:underline}.site-header nav .nav-link{border:1px solid var(--ink);padding:13px 18px;border-radius:7px}.nav-link span{margin-left:18px}.block-section{padding:70px 0}.block-section:first-child{padding:74px 0 82px}.section-inner>.block-text:first-child{font-size:12px;font-weight:700;letter-spacing:2px;text-transform:uppercase;color:color-mix(in srgb,var(--accent) 68%,currentColor);margin:0 0 30px}h1,h2,h3{letter-spacing:-.05em;line-height:1.05;margin:0 0 24px}h1{font-size:clamp(48px,6.6vw,84px);font-weight:500;max-width:900px}h2{font-size:clamp(32px,4vw,49px);font-weight:500;max-width:760px}h3{font-size:26px;letter-spacing:-.035em;line-height:1.2}.block-text{font-size:18px;line-height:1.7;max-width:690px;margin:0 0 23px;color:color-mix(in srgb,currentColor 82%,transparent);white-space:pre-line}.button{display:inline-flex;align-items:center;gap:32px;min-height:50px;margin-top:9px;padding:15px 21px;background:var(--ink);color:var(--paper);border-radius:7px;text-decoration:none;font-size:14px;font-weight:600}.button:hover{background:color-mix(in srgb,var(--ink) 88%,var(--paper))}.block-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(250px,100%),1fr));gap:22px;margin-top:38px;background:transparent!important}.block-column{min-width:0}.block-column .block-text{font-size:15px}.block-column>.block-text:first-child{font-size:12px;font-weight:700;letter-spacing:1px;color:color-mix(in srgb,var(--accent) 68%,currentColor);margin-bottom:34px}.tone-mint{background:var(--mint)}.tone-ink{background:var(--ink);color:var(--paper)}.tone-ink .block-text{color:color-mix(in srgb,var(--paper) 86%,var(--ink))}.tone-ink .section-inner>.block-text:first-child{color:var(--mint)}.tone-ink .button{color:var(--ink);background:var(--mint)}.width-narrow>.section-inner{max-width:800px}.site-footer{border-top:1px solid var(--line)}.site-footer p,.site-footer>span{font-size:12px;color:var(--muted)}.site-footer .brand{font-size:22px}.skip-link{position:fixed;left:16px;top:-100px;z-index:10;background:var(--ink);color:white;padding:16px}.skip-link:focus{top:16px}img{display:block;max-width:100%;height:auto;border-radius:12px}.error-page{padding-block:100px}.error-page h1{max-width:800px}
@media(max-width:650px){.shell{width:calc(100% - 36px)}.site-header nav{width:100%;justify-content:space-between;gap:12px;font-size:12px}.site-header nav .nav-link{padding:10px}.nav-link span{margin-left:6px}.block-section,.block-section:first-child{padding-block:48px}h1{font-size:49px}.block-text{font-size:16px}.site-footer{align-items:flex-start;flex-direction:column;gap:10px}.site-footer p{margin:0}.block-row{gap:16px}}
/* Menus. Submenus open with details/summary, so the navigation works without JavaScript. */
.menu-list{list-style:none;display:flex;flex-wrap:wrap;align-items:center;gap:4px 22px;margin:0;padding:0}
@@ -39,7 +39,7 @@ p{hyphens:auto}
/* Blocks introduced with schema v6. No JavaScript: the accordion is details/summary. */
.block-icon{flex-shrink:0;display:block}
.block-iconcard{display:grid;gap:12px;padding:26px;border:1px solid currentColor;border-radius:12px}
.kant .block-iconcard h3{margin:0;font-size:19px;line-height:1.3}
.block-iconcard + .block-iconcard,.block-quote + .block-quote,.block-cta + .block-cta,.block-iconcard + .block-quote,.block-quote + .block-iconcard{margin-top:18px}.kant .block-iconcard h3{margin:0;font-size:19px;line-height:1.3}
.block-iconcard h3 a{text-decoration:none}
.block-iconcard h3 a:hover{text-decoration:underline}
.block-iconcard p{margin:0;line-height:1.65}
@@ -54,7 +54,7 @@ p{hyphens:auto}
.quote-rating{margin:0;letter-spacing:3px}
.block-logos{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));align-items:center;gap:24px}
.block-logos img{max-height:52px;width:auto;margin-inline:auto;display:block}
.block-cta{display:flex;flex-wrap:wrap;gap:20px 34px;align-items:center;justify-content:space-between;padding:32px;border:1px solid currentColor;border-radius:14px}
.kant .block-form button[type=submit]{background:var(--accent);color:#fff;border-color:var(--accent)}.kant .block-form button[type=submit]:hover{background:color-mix(in srgb,var(--accent) 86%,#000)}.kant .block-cta .button{background:var(--accent);color:#fff;border-color:var(--accent)}.kant .block-cta .button:hover{background:color-mix(in srgb,var(--accent) 86%,#000)}.block-cta{display:flex;flex-wrap:wrap;gap:20px 34px;align-items:center;justify-content:space-between;padding:32px;border:1px solid currentColor;border-radius:14px}
.kant .block-cta h2{margin:0 0 6px;font-size:26px;letter-spacing:-.6px}
.block-cta p{margin:0;line-height:1.6}
.block-contact{display:grid;gap:14px;font-style:normal}
+32
View File
@@ -2227,6 +2227,38 @@ final class CmsTest extends TestCase
self::assertStringNotContainsString('outline:3px solid #b85c12', $site, 'Auch der Fokusring folgt der Palette.');
}
public function testTextForScreenReadersStaysOutOfSight(): void
{
// Der peinlichste Fund der Reihe: die Klasse für ausschließlich vorgelesenen Text gab es
// in der Verwaltung, aber nicht auf den öffentlichen Seiten. Damit stand „Bewertung: 5
// von 5“ neben den Sternen und „(Pflichtfeld)“ hinter jedem Feld sichtbar auf der Seite.
// axe hat das nie gemeldet, weil es prüft, ob etwas angesagt wird - nicht, ob etwas
// fälschlich zu sehen ist.
$css = (string) file_get_contents(__DIR__.'/../public/assets/site.css');
self::assertStringContainsString('.visually-hidden{position:absolute', $css);
self::assertStringContainsString('clip-path:inset(50%)', $css);
// Und die Stellen, die sich darauf verlassen, gibt es wirklich.
$document = DemoContent::starter('Mit Stimme');
$document['schemaVersion'] = 17;
$document['blocks'][] = ['id' => 'stimme', 'type' => 'quote', 'props' => ['text' => 'Ein Satz.', 'author' => 'Name', 'role' => '', 'rating' => 5]];
$this->pages->publish('page-1', $this->pages->saveDraft('page-1', $document, 0), 1);
self::assertStringContainsString('<span class="visually-hidden">Bewertung: 5 von 5</span>', $this->request('/')->getContent());
}
public function testAColumnIsALayoutDeviceAndNotABox(): void
{
// Eine Spalte brachte Rahmen, Ecken und 30 px Innenabstand mit. Wer zwei Texte
// nebeneinander stellte, bekam zwei Kästchen. Die Karte ist eine Einstellung, kein
// Standard - und der Knopf im Aufruf-Band nimmt die Akzentfarbe, weil er auf einem
// dunklen Band vorher Tinte auf Tinte war.
$css = (string) file_get_contents(__DIR__.'/../public/assets/site.css');
self::assertStringContainsString('.block-column{min-width:0}', $css);
self::assertStringNotContainsString('.block-column{border:1px solid', $css);
self::assertStringContainsString('.kant .block-cta .button{background:var(--accent)', $css);
self::assertStringContainsString('.block-iconcard + .block-iconcard', $css, 'Gestapelte Karten brauchen Abstand.');
}
public function testHeadingsAreNotHyphenated(): void
{
// Beim Nachbau von kant-it.de stand in der Überschrift „di-gitale Exzellenz“: `hyphens:auto`