Files
KantSites/docs/STATUS.md
T
kantarciandClaude Fable 5.1 32ee28888e
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
Drag the border between two columns to set their widths in 1.16.0
Umut, with a card of an icon next to a text as the example: "bei Rows die
Breite ändern, am besten per Drag and Drop im Editor". Rows only ever had
equal columns - both the classic `columns` token and the free `gridColumns`
produce `repeat(n, 1fr)`.

A row now carries the free style `columnWidths`, a list of shares such as
`2 1` or `66.7 33.3`, per device and without a new document schema.
`StyleCompiler::columnTracks()` turns it into `grid-template-columns`,
pads or cuts the shares to the number of columns, and gives a device whose
layout stacks the columns a single track, because the desktop rule carries
an id and would otherwise outrank the class that stacks a phone.

In the editor the border between two side-by-side columns of the selected
row is a handle: dragging restyles the row inline while the pointer moves
and writes one undo step on release; the arrow keys nudge by five points.
The design panel shows the same value as the text field "Spaltenbreiten".
The Playwright editor check drags the handle with the real mouse.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 10:39:06 +02:00

227 KiB
Raw Blame History

Projektstatus

1.16.0 – Spaltenbreiten einer Zeile durch Ziehen (15.09.2026)

Anlass: Umut, mit der Karte „HausKlar“ (Symbol links, Text rechts) als Beispiel: „bei Rows die Breite ändern, am besten per Drag and Drop im Editor.“ Zeilen kannten bisher nur gleich breite Spalten. Entscheidung und Begründung: ADR 098.

Umsetzung: Neue freie Stileigenschaft columnWidths an der Zeile (Typ fractions, StyleSchema::FRACTIONS, z. B. 66.7 33.3), je Gerät, ohne neues Dokumentschema. StyleCompiler::columnTracks() macht daraus grid-template-columns:minmax(0,66.7fr) minmax(0,33.3fr), füllt fehlende Anteile mit dem Durchschnitt auf, kappt überzählige und setzt ein Gerät, dessen Aufteilung stapelt (one oder gridColumns 1), auf eine Spur – sonst schlüge die ID-Regel des Desktops die Stapel-Klasse des Handys. Im Editor (canvas-overlay.js) liegt auf jeder Grenze zwischen zwei nebeneinander stehenden Spalten der gewählten Zeile (oder der Zeile um die gewählte Spalte) ein Griff .ov-resize: Ziehen stellt die Zeile in der Vorschau live um, Loslassen schreibt einen Verlaufsschritt (onResize in editor.js → setStyle), Pfeiltasten verschieben um fünf Punkte und behalten den Fokus. Ausgangswert sind die gemessenen Spaltenbreiten. Im Design-Bedienfeld gibt es dazu das Textfeld „Spaltenbreiten“ (Layout, nur bei Zeilen). Kein Griff bei gestapelten Spalten und während ein Element gezogen wird.

Geprüft: phpunit vollständig mit PHP 8.4 512 von 512 grün, mit PHP 8.2 512 grün mit der vorbestehenden Warnung in UpdaterTest; neu tests/ColumnWidthsTest.php (Spuren, Stapeln je Gerät, Auffüllen und Kappen, Ablehnung ungültiger Werte). npm test 33 grün (Relevanz nur für Zeilen). npm run test:editor mit PHP_BINARY=C:/php84/php.exe vollständig bestanden – neu darin: echter Mauszug am Griff verbreitert die erste Spalte, der Wert steht im Bedienfeld der Zeile, auf „Mobil“ gibt es keinen Griff, ein Rückgängig stellt gleiche Spalten her (reports/editor/column-widths.png). php-cs-fixer ohne Befund. Ein erster Lauf des Checks scheiterte an openGroup, das im Skript erst später definiert ist – die Gruppe wird jetzt an Ort und Stelle geöffnet.

Nebenbei: .claude/launch.json trägt in der Arbeitskopie den noch nicht committeten Eintrag maler-drews einer anderen Sitzung auf Port 8091; die Pilotkonfiguration ist deshalb lokal auf 8092 verschoben, die Datei bleibt aus diesem Commit heraus.

1.15.1 veröffentlicht (15.09.2026)

Tag v1.15.1 (Commit d05dba3), Release: https://git.kant-it.de/kantarci/KantSites/releases/tag/v1.15.1 – eigene Absenderadresse je Formular.

Gebaut aus einem sauberen Checkout des Tags (git worktree add var/release-source-1.15.1 v1.15.1, php scripts/build-release.php darin): 227 Quelldateien, vendor/ ohne Entwicklungspakete, 3549 Dateien im Archiv.

Anhänge: kantsites-1.15.1.zip (4,7 MB, SHA-256 e15a7a69f299c16d86d372b65610b52f15b40b93904fa6402ea6991de7ed21f7), .sha256, install.php mit eingetragener Prüfsumme; hochgeladen mit var/publish-release-1.15.1.py, erst Entwurf, dann veröffentlicht.

Geprüft: HTTP-Archiv-Update 1.15.0 → 1.15.1 ok (Update 302, System 200, Abschluss 302, öffentliche Seite 200; scripts/archive-update-check.mjs); nach der Veröffentlichung ohne Anmeldung alle drei Anhänge bytegleich, latest = v1.15.1. Die Prüfungen des Codes selbst stehen im Eintrag darunter.

Nächster Schritt: Update auf web.kant-it.de über System → Update aus dem Archiv (keine Migration), dann unter Formulare eine Absenderadresse der eigenen Domain eintragen und eine Testanfrage senden.

1.15.1 – Eigene Absenderadresse je Formular (15.09.2026)

Anlass: Umut: „Versandadresse bei Kontaktformularen sollte man noch festlegen können – aktuell wird's über die gleiche Adresse verschickt wie der Empfänger?" Stimmte: FormController::deliver() setzte From auf die Empfängeradresse. Das geht schief, sobald der Empfänger ein Postfach bei einem anderen Anbieter ist (z. B. eine GMX- oder Gmail-Adresse), weil viele Hoster nur zustellen, was von einer Adresse der eigenen Domain kommt, und SPF/DMARC des fremden Anbieters die Post als Fälschung abweisen.

Umsetzung: Neue Formulareinstellung sender (freiwillig). FormRepository::validateSettings() prüft sie wie den Empfänger mit FILTER_VALIDATE_EMAIL, leer bleibt erlaubt; die Verwaltung (forms.html.twig, Abschnitt Zustellung, Feld „Absender“ direkt unter dem Empfänger mit Hinweis) und FormAdminController reichen sie durch. Beim Versand gilt sender ?: recipient – bestehende Formulare ohne den Schlüssel verhalten sich wie bisher, es ist keine Migration nötig. Die Antwortadresse aus dem Formularfeld bleibt Reply-To.

Geprüft: phpunit vollständig mit PHP 8.4 509 von 509 grün, mit PHP 8.2 509 grün mit der vorbestehenden Warnung in UpdaterTest; npm test 33 grün; npm run test:editor mit PHP_BINARY=C:/php84/php.exe vollständig bestanden, darin die axe-Prüfung der Formularseite. Neu in EditorSecurityTest: testAFormMaySendFromItsOwnAddress (From = Absender, To = Empfänger, Reply-To = Feld; ungültiger Absender abgewiesen, Leerzeichen werden zu leer) und im bestehenden Absendetest die Prüfung, dass ohne Absender From = To ist – beides über getMailerMessage() mit dem null://-Transport der Tests. php-cs-fixer ohne Befund. In der Pilotkopie im Browser (kantsites-pilot, jetzt Port 8091, weil 8087 von einer anderen Sitzung belegt war): Feld sichtbar, formular@kant-it.de gespeichert und nach dem Neuladen erhalten, Meldung „Das Formular wurde gespeichert.“ Ein echter Versand über SMTP wurde nicht geprüft.

Nächster Schritt: Auf web.kant-it.de nach dem nächsten Update unter Formulare eine Absenderadresse der eigenen Domain eintragen (z. B. formular@kant-it.de), dann eine Testanfrage senden und im Postfach Absender und Zustellung kontrollieren.

1.15.0 veröffentlicht (15.09.2026)

Tag v1.15.0 (Commit 160c394), Release: https://git.kant-it.de/kantarci/KantSites/releases/tag/v1.15.0 – Formulare, Statistik, Design und Seitenleiste der Verwaltung überarbeitet, Formulare mit eigenen Farben und Vorschau.

Gebaut aus einem sauberen Checkout des Tags (git worktree add var/release-source-1.15.0 v1.15.0, php scripts/build-release.php darin): 227 Quelldateien, vendor/ ohne Entwicklungspakete, 3549 Dateien im Archiv.

Anhänge: kantsites-1.15.0.zip (4,7 MB, SHA-256 86ef728812db3b2f565f07dd1d8aebf8d5d41b75875695544134684b77a193ed), .sha256, install.php mit eingetragener Prüfsumme; hochgeladen mit var/publish-release-1.15.0.py, erst Entwurf, dann veröffentlicht.

Geprüft: HTTP-Archiv-Update 1.14.2 → 1.15.0 ok (Update 302, System 200, Abschluss 302, öffentliche Seite 200; scripts/archive-update-check.mjs, aus dem Hauptrepository heraus, weil der saubere Checkout weder vendor/ noch var/kantit.db hat); nach der Veröffentlichung ohne Anmeldung alle drei Anhänge bytegleich, latest = v1.15.0, Installeradresse 200. Die Prüfungen des Codes selbst stehen im Eintrag darunter.

Nächster Schritt: Update auf web.kant-it.de über System → Update aus dem Archiv (keine Migration), dann unter Formulare das Aussehen des Kontaktformulars wählen.

1.15.0 – Formulare, Statistik, Design und Seitenleiste überarbeitet (15.09.2026)

Anlass: Umut fand die Formularanlage unschön und wollte Felder löschen können, die Statistik „lieblos“, auf der Designseite vordefinierte Farben, eigene Farben für Link- und Seitenfarbe und eine Vorschau der Schriften, und den Fuß der Seitenleiste (Fassung, Benutzer, Abmelden) besser. Nachgeschoben: eine Vorschau des Kontaktformulars, in der Rahmen-, Hintergrund- und Buttonfarbe direkt gewählt werden.

Formulare (forms.html.twig, neu form-editor.js). Jedes Feld ist eine Karte mit Nummer, Beschriftung, Typ, technischem Name, Hilfetext, Pflicht-Schalter und einem Entfernen-Knopf; „Feld hinzufügen“ klont eine <template>-Karte. Der technische Name folgt der Beschriftung (Größe der Fläche → groesse_der_flaeche), bis jemand ihn selbst schreibt; Optionen erscheinen nur bei einer Auswahl; eine Einwilligung ist sichtbar immer Pflicht. Ohne Skript bleibt alles bedienbar: eine Karte ohne Beschriftung wird nicht gespeichert. Neu ist der Abschnitt Aussehen: Buttonbeschriftung, Rahmen und Hintergrund der Felder, Button- und Buttonschriftfarbe, jeweils aus der Palette oder als eigener Wert (FormRepository::COLORS, geprüft mit StyleSchema::COLOR). Daneben eine Vorschau, die jeder Eingabe folgt und den Absendeknopf mit dem Standardbutton der Website zeichnet (buttonSample). Auf der Website schreibt FormRepository::css() je Formular eine Regel .block-form[data-form="…"]{…} in den gehashten Stylesblock (PageController::renderPage); Felder lesen --form-border/--form-bg (site.css), der Button bekommt --kb-fill/--kb-ink und die Darstellungsvariablen neu – am Wurzelelement wären sie schon mit den alten Werten aufgelöst. Das „Neues Formular“ ist ein zugeklapptes Karten-Details am Ende.

Statistik (statistics.html.twig, Statistics::overview()). Sechs Kennzahlkacheln: Aufrufe mit Vergleich zum gleich langen Zeitraum davor (previousTotal), Schnitt je Tag, stärkster Tag mit Datum und Wochentag (busiestDay), aufgerufene Seiten (pageCount), Aufrufe über Verweise (referred) und nicht gefundene Adressen (missingTotal). Der Verlauf hat Rasterlinien, jeder Balken einen <title> mit Tag und Zahl, der stärkste Tag ist hervorgehoben; dazu ein neues Wochentagsdiagramm im Schnitt (weekdays). Die Ranglisten tragen Rang und einen Anteilsbalken (SVG-Breite, kein Inline-Stil). Der Zeitraum ist eine Leiste mit gedrücktem Knopf in der Kopfzeile; „Was diese Zahlen nicht sagen“ ist zugeklappt.

Design (theme.html.twig, neu theme-panel.js). Die Palette sind Farbkarten, deren Fläche der Farbwähler selbst ist, mit Hexwert, Name, Schlüssel und Entfernen. Sieben fertige Paletten (Theme::PALETTES: Wald, Ozean, Sand, Koralle, Schiefer, Olive, Nacht; die Linkfarbe erreicht auf dem Papierton mindestens 4,5:1) ersetzen die fünf Grundschlüssel und lassen eigene Farben stehen. Schriften sind Karten mit einer Probe in der Schrift selbst (lokale fonts.css wird geladen, wenn vorhanden – FontLibrary::hasStylesheet()), Schnitte als Chips, die Auswahlen für Fließtext/Überschriften/Button folgen neuen Schriften. Text-, Seiten- und Linkfarbe nutzen das Palettenbedienelement des Editors (createColorControl), das bei jeder Palettenänderung neu aufgebaut wird; daneben eine Typografie-Vorschau mit Überschriftenskala, Zeilenhöhe, Farben und Unterstreichung.

Seitenleiste (shell.html.twig). Unten eine Kontokarte: Avatar, Name, Rolle (Administrator/Veröffentlicher/Redakteur), rechts ein Abmeldesymbol mit Beschriftung für Vorlesesoftware; darunter „KantSites 1.14.2“ mit dem Update-Hinweis in einer Zeile. Neue Symbole logout, plus, remove. Auf Tablets wischt nur noch die Liste, Konto und Abmelden bleiben rechts im Bild; dafür braucht die Rasterspalte minmax(0, 1fr), sonst schob die Leiste die Seite über den Rand.

Die Formularvorschau nennt den Kontrast der Buttonschrift auf der Buttonfarbe (WCAG-Formel, grün ab 4,5:1, gelb ab 3:1, sonst rot), damit eine unlesbare Wahl auffällt, bevor sie auf der Website steht. Die pattern-Attribute der Schlüsselfelder maskieren den Bindestrich ([a-z][a-z0-9\-]*): Chrome prüft pattern inzwischen mit dem v-Flag und lehnte das alte Muster mit einem Konsolenfehler ab – die Prüfung war damit wirkungslos.

Geprüft: phpunit mit PHP 8.4 508 von 508 grün, mit PHP 8.2 508 grün mit einer vorbestehenden Warnung in UpdaterTest (Git-Umgebung); neu getestet: Formularfarben auf der Seite samt CSP-Hash, Ablehnung ungültiger Farben, Statistiksummen und Vorzeitraum, drei Diagramme mit Titeln. npm run test:editor vollständig bestanden – darin die axe-Prüfung von Formularen, Statistik und Design. test:buttons besteht den Designseiten-Teil (Vorlage wählen, speichern, Werte erhalten) und scheitert danach wie test:navigation-ux an einem Klick auf den Strukturbaum des Editors (#structure [data-block-id=…] „not visible“) – eine Stelle außerhalb dieser Änderung, nicht weiter untersucht. test:admin scheitert vor den Formularen an zwei vorbestehenden Befunden: dem Wiedergabeknopf des öffentlichen Sliders bei reduzierter Bewegung und nested-interactive in den Menü-Zusammenfassungen (Ziehgriff im summary). In der Pilotkopie im Browser: Feld hinzufügen/entfernen, Namensableitung, Farbwahl mit Vorschau und Kontrasthinweis, Speichern, Regel und Buttonfarbe auf der öffentlichen Startseite; Palette „Ozean“ übernehmen, Schrift hinzufügen, Linkfarbe wählen; Seitenleiste und Bildschirme auf 1440, 900 und 375 px. Codestil mit php-cs-fixer (verbleibende Abweichung in tests/CmsTest.php ist vorbestehend). Veröffentlicht als 1.15.0, siehe Eintrag oben.

1.14.2 veröffentlicht (15.09.2026)

Tag v1.14.2 (Commit 0aa1ff6), Release: https://git.kant-it.de/kantarci/KantSites/releases/tag/v1.14.2 – Fußzeile des Einwilligungshinweises: Einzelwahl links, Rechtslinks rechts.

Gebaut aus einem sauberen Checkout des Tags (git worktree add var/release-source-1.14.2 v1.14.2), nicht aus der Arbeitskopie: dort lagen zu diesem Zeitpunkt nicht committete Änderungen einer anderen Sitzung (site.css, archive.html.twig, page.html.twig, tests/CmsTest.php, neu src/Twig/HeaderExtension.php), und ein erster Build aus der Arbeitskopie hatte sie mitgenommen – der wurde verworfen. Das Archiv enthält keine vorgefertigten assets/build/-Bündel mehr (die Arbeitskopie hatte welche, sie entstehen beim ersten Aufruf); daher 3546 statt 3552 Dateien.

Anhänge: kantsites-1.14.2.zip (4,7 MB, SHA-256 236dff086267591a51198b55ddfbaf2ee999db8a081ec5bb1d28752260a3db3f), .sha256, install.php; hochgeladen mit var/publish-release-1.14.2.py, erst Entwurf, dann veröffentlicht.

Geprüft: PHP 506 Tests grün (ein erster Lauf zeigte einen Fehler, während die andere Sitzung tests/CmsTest.php bearbeitete; zwei weitere Läufe grün); HTTP-Archiv-Update 1.14.1 → 1.14.2 ok; nach der Veröffentlichung ohne Anmeldung alle drei Anhänge bytegleich, latest = v1.14.2, Installeradresse 200.

1.14.2 – Einwilligungshinweis: Fußzeile aufgeräumt (15.09.2026)

Anlass: Umut fand „Einzeln wählen“ und die Rechtslinks unordentlich und schlug links/rechts vor.

Umgesetzt (consent-banner.html.twig, site.css): Beide stehen in einer Zeile .consent-foot – Einzelwahl links, Datenschutz · Impressum rechts, auf gemeinsamer Grundlinie. Aufgeklappt nimmt die Einzelwahl die volle Breite ([open]{flex-basis:100%}), die Links rutschen rechtsbündig darunter. Die Links stehen ohne Punkt dazwischen mit 14 px Abstand; ohne beide Adressen entfällt die Zeile. Ein Stolperstein: .consent-bar p schlug .consent-links und nahm den rechten Rand weg – der Selektor heißt jetzt .consent-bar p.consent-links.

Geprüft: vier Gestaltungsvarianten auf 1440 und 390 px, zu- und aufgeklappt (Screenshots reports/consent-look/*-offen.png), sechs Einwilligungstests grün. Veröffentlicht als 1.14.2, siehe Eintrag oben.

1.14.1 veröffentlicht (15.09.2026)

Tag v1.14.1 (Commit 0bdcc7f), Release: https://git.kant-it.de/kantarci/KantSites/releases/tag/v1.14.1. Enthält den gestaltbaren Einwilligungshinweis, den bei offener Frage erreichbaren Nach-oben-Knopf, den korrigierten Abstand unten und die orangen Beschriftungen im Editor; dazu 860bed9 (Seed-Skript Tagespflege Moldovan, nicht im Archiv).

Anhänge: kantsites-1.14.1.zip (4,7 MB, 3552 Dateien, SHA-256 c0620a3155db894373199dbe2e286744dd7b21eed2c355e8d2a890652d1d203e), .sha256, install.php mit eingetragener Prüfsumme; hochgeladen mit var/publish-release-1.14.1.py, erst Entwurf, dann veröffentlicht.

Geprüft: PHP 505 Tests grün; browser-check.mjs, editor-check.mjs bestanden; HTTP-Archiv-Update 1.14.0 → 1.14.1 ok (Update 302, System 200, Abschluss 302, öffentliche Seite 200). Nach der Veröffentlichung ohne Anmeldung: alle drei Anhänge bytegleich, /releases/latest nennt v1.14.1, Installeradresse antwortet 200.

Nächster Schritt: Update auf web.kant-it.de über System → Update aus dem Archiv, Migrationen abschließen (keine neuen), dann unter Einstellungen → Einwilligung das Aussehen wählen; var/kantit.db mit dem korrigierten Kopfmenü hochladen.

1.14.1 – Einwilligungshinweis gestaltbar, Nach-oben-Knopf bei offener Frage, orange Beschriftungen (15.09.2026)

Anlass: Umut wollte das Banner gestaltbar (rund, Farben) und schöner, den Abstand unten korrigiert, belegte Eigenschaften im Editor orange statt nur fett mit Punkt, und Gerätechips, die sich „aktiv überlagern“.

Einwilligungshinweis (ADR 097). Sieben neue Einstellungen: consentLayout (karte/leiste/ecke), consentShape (weich/rund/eckig), consentTitle, consentColor, consentInk, consentAccent, consentAccentInk; Farben mit StyleSchema::COLOR geprüft, Palette über Theme::cssColor() (neu, ersetzt die Kopie im Nach-oben-Knopf). Consent::styles() schreibt :root{--kant-consent-…} in den gehashten Stylesblock; die Anordnung ist eine Klasse. Neues Grundlayout: Streifen und Symbol in der Knopffarbe, gleich breite Knöpfe, aufklappbares „Einzeln wählen“ mit Pfeil, unterstrichene Rechtslinks; Leiste auf breiten Bildschirmen zweispaltig. Archiv und /einwilligung bekommen Theme- und Hinweisvariablen als gehashten Stylesblock – das Archiv trug bisher gar keine Palette.

Abstand unten: 320 px bis 620 px (Leiste bis 299 px hoch), 270 px bis 899 px, 200 px darüber; die Fläche scrollt ab 100dvh − 24px in sich. Nach-oben-Knopf hebt sich bei Leiste und Ecke auf jeder Breite (:has()), bei der Karte bis 799 px.

Editor: belegte Beschriftungen in --used (#9d4e0d, 5,6:1); Chips mit 6 px Lücke, Fokusring ohne Versatz, Gruppe flex:none. Ein Überlagern der Chips ließ sich in 1.14.0 nicht reproduzieren – am Handy (390 px) liegen die drei Chips bei 22 px Breite mit 6 px Abstand, kein outline gezeichnet; die Rückmeldung bezog sich wohl auf den outline-Ring von 1.13.0.

Geprüft: PHP 505 Tests grün, darunter neu testTheConsentQuestionTakesLayoutShapeAndColoursFromTheSettings (Standard, eigene Werte, unbekannter Palettenschlüssel fällt weg, Einwilligungsseite mit Hash, ungültige Farbe abgewiesen). Chromium: vier Varianten (Karte, Leiste, Ecke rund dunkel, Karte eckig dunkel) auf 1440 und 390 px – Klasse, Radien, Farben und Überschrift stimmen, Nach-oben-Knopf sichtbar, treffbar, ohne Überlappung; /einwilligung 200 mit gehashtem style-src. Nach-oben-Prüfung auf acht Breiten bestanden. browser-check.mjs, editor-check.mjs (Einstellungen, Einwilligung) und axe über die Editorseitenleiste bestanden. Screenshots: reports/consent-look/, reports/admin-hints/.

Nicht geprüft: echtes iOS-Safari; der @supports not-Zweig; Lighthouse.

Nach-oben-Knopf bei offener Einwilligungsfrage (15.09.2026)

Anlass: Umut meldete, der Nach-oben-Knopf funktioniere auf dem Handy nicht.

Befund. Der Knopf selbst funktioniert auf dem Telefon: In iPhone- und Pixel-Emulation (Chromium), lokal wie auf web.kant-it.de (dort bereits 1.14.0 mit scrolled.js), erscheint er nach 500 px, ist treffbar, und Tippen springt nach oben – sobald die Einwilligungsfrage beantwortet ist. Vorher galt body.consent-open .scroll-top{display:none} auf allen Geräten. Live trägt jeder Erstbesuch consent-open, und auf dem Telefon bleibt die Leiste unten oft einfach stehen – der Knopf kam dort nie. Kein Fehler im Skript und nicht der alte Safari-Fehler aus 1.13.0.

Korrektur (site.css, page.html.twig). Der Knopf bleibt bei offener Frage sichtbar. Gemessen deckt die Leiste die rechte Ecke nur bis knapp 800 px ab (bis 620 px volle Breite, 262–285 px hoch; darüber zentriert mit höchstens 620 px). In dieser Stufe setzt sich der Knopf per Ankerpositionierung 12 px auf die Oberkante der Leiste (anchor-name:--kant-consent, bottom:calc(anchor(top) + 12px)), unabhängig davon, wie hoch ihr Text umbricht. Browser ohne Ankerpositionierung blenden ihn dort weiter aus (@supports not). Ab 800 px bleibt er an seinem Platz. Weil ein Anker im Dokument vor dem positionierten Element stehen muss, gibt die Vorlage die Leiste jetzt vor dem Knopf aus.

Geprüft: Chromium, isolierte Kopie der Pilotdatenbank, Einwilligung offen auf 360, 390, 700, 768, 799, 800 und 1440 px sowie beantwortet auf 390 px – in allen acht Fällen sichtbar, keine Überlappung mit der Leiste, treffbar, nach dem Tippen scrollY 0. Twig-Lint gültig. PHP-Tests 504 grün, browser-check.mjs und editor-check.mjs (einschließlich Einwilligungsabläufe) bestanden.

Nicht geprüft: echtes iOS-Safari – WebKit ist für Playwright auf diesem Rechner nicht installiert. Der @supports not-Zweig wurde nicht ausgeführt, weil Chromium Ankerpositionierung kann.

Nebenbefund, nicht geändert: Bis 620 px reserviert body.consent-open 260 px Abstand unten, die Leiste ist mit Standardtext bis 285 px hoch – auf kleinen Telefonen liegen die letzten rund 25 px der Seite unter der offenen Leiste.

Nächster Schritt: nicht committet und nicht veröffentlicht; live erst mit einem Release (z. B. 1.14.1).

1.14.0 veröffentlicht (15.09.2026)

Tag v1.14.0 (Commit a434e0c), Release: https://git.kant-it.de/kantarci/KantSites/releases/tag/v1.14.0. Enthält die Schriften vom eigenen Server, den Hinweisstreifen, System und Update auf dem Telefon, die Gerätechips sowie Öffnungszeiten, Einsatzgebiet und FAQ in den strukturierten Daten.

Anhänge: kantsites-1.14.0.zip (4,7 MB, SHA-256 8360566b1eaed670b29f506262953d3b17f95893e23c7f4a940ff6d5e3f62735), .sha256, install.php mit eingetragener Prüfsumme. Hochgeladen mit var/publish-release-1.14.0.py über den Git-Zugang dieses Rechners, erst als Entwurf, dann veröffentlicht.

Geprüft: 504 PHP-Tests grün; browser-check.mjs und editor-check.mjs bestanden; HTTP-Archiv-Update 1.12.1 → 1.14.0 ok. Nach der Veröffentlichung ohne Anmeldung: alle drei Anhänge bytegleich mit den lokalen Dateien, /releases/latest nennt v1.14.0, die Installeradresse …/releases/download/v1.14.0/kantsites-1.14.0.zip antwortet mit 200.

Nächster Schritt: Update auf web.kant-it.de über System → Update aus dem Archiv, danach Migrationen abschließen und var/kantit.db mit dem korrigierten Kopfmenü hochladen.

Globale Hinweise, der Schriftenknopf und die Gerätechips (15.09.2026)

Anlass: Umut fehlte ein Hinweis, der auch außerhalb der Systemseite sagt, dass etwas zu tun ist; der Knopf „Schriften holen“ sah nicht aus wie die übrigen; und im Designbereich störte ein roter Rahmen an den Gerätechips, der abgeschnitten wirkte.

Hinweisstreifen auf jedem Verwaltungsbildschirm (kant_admin_notices(), admin/shell.html.twig). Über dem Inhalt steht jetzt, was einen Knopf braucht, und der Knopf gleich daneben: „Fassung X ist verfügbar“ → Jetzt aktualisieren (System), „Schriftdateien fehlen: …“ → Schriften holen (Design). Keine Zeile davon geht ins Netz – die verfügbare Fassung steht im Protokoll der täglichen Aufgabe update-check, die fehlenden Schriften sind ein Blick in den Ordner. Der Updatehinweis erscheint nur für ROLE_ADMIN, weil nur die auf die Systemseite dürfen. Warm statt rot: es ist eine Aufgabe, kein Fehler. Auf 390 px bricht der Streifen um, der Knopf rutscht unter den Text.

Schriftenknopf. Er trug gar keine Klasse und war deshalb der einzige Knopf der Verwaltung im Standardaussehen des Browsers. Jetzt primary (bzw. secondary beim erneuten Holen), und der ganze Abschnitt steht wie alles andere in einer Karte mit Kopf und Körper.

Gerätechips D/T/M (editor.css). Der rote Kasten war outline:2px mit outline-offset:1px auf dem gerade bearbeiteten Gerät. Ein outline liegt außerhalb des Kästchens: bei 2 px Abstand lief er unter den Nachbarchip und wurde von der seitlich beschnittenen Sidebar gekappt – daher der abgeschnittene Eindruck. Jetzt zwei Zustände mit zwei Mitteln:

  • belegt (dieses Gerät trägt einen eigenen Wert): gefülltes Orange --used: #9d4e0d, weiße Schrift – 5,6:1 Kontrast. Dieselbe Farbe trägt der Punkt vor der Beschriftung, damit „hier steht ein eigener Wert“ überall gleich aussieht.
  • gerade bearbeitet: 2 px Rahmen innen (border-width, kein outline), auf belegten Chips zusätzlich ein weißer Innenring. Nichts ragt mehr aus dem Kästchen, nichts wird beschnitten.

Chips von 19 auf 22 px, Abstand von 2 auf 4 px – auf einen Blick lesbar, welche Geräte an einer Eigenschaft hängen.

Geprüft (isolierte Kopie der Pilotdatenbank, künstlich auf „Lato 300 fehlt“ und „Fassung 9.9.9 verfügbar“ gesetzt): beide Streifen erscheinen auf /admin, tragen die richtigen Ziele, und auf 390 px bleibt die Dokumentbreite bei 390. Schriftenknopf trägt primary. Chips: belegte auf rgb(157,78,13), bearbeitete mit 2 px Rahmen, kein gezeichnetes outline mehr. axe über die Editorseitenleiste ohne Verstöße. PHP-Tests: 498, ein bekannter Fehler (testAnyPublishedPageCanBecomeTheRootAndEverythingFollows). Screenshots: reports/admin-hints/.

Zu beachten: admin.css und editor.css hängen an ?v={{ kant_version() }}. Bis die VERSION steigt, liefert der Browser die alten Fassungen aus dem Zwischenspeicher.

Schriften nur noch vom eigenen Server, kein sichtbarer Schriftwechsel mehr (14.09.2026)

Anlass: Umut sah im Browser, wie die Schrift kurz von fett auf dünn sprang, und stellte zwei Regeln auf: keine Schriften über Adressen von Google, und kein Nachladen nach dem fertigen Seitenaufbau.

Zwei getrennte Ursachen, beide belegt.

  1. Der späte Schnitt. FontLibrary::preload() holte je Familie nur die ersten zwei Schnitte vorab. Das Kant-IT-Theme nennt Lato 400, 700 und 900; die Überschriften stehen auf 800/900 und landen damit auf Lato 900 – dem einzigen Schnitt ohne Vorabholen. Gemessen am Nachbau: lato-400 und lato-700 starteten bei 151 ms, lato-900 erst bei 156 ms, also nach dem Stylesheet. Mit font-display:swap zeichnet der Browser die Überschrift bis dahin in der Ersatzschrift und tauscht sie danach – der gemeldete Sprung. Jetzt wird jeder Schnitt des Themes vorab geholt (lateinischer Grundbereich; die erweiterten Bereiche bleiben draußen).
  2. Google in der Editorvorschau. EditorController und PartialController gaben der Vorlage googleFonts und kein localFonts. In der Vorschau kamen die Schriften damit bei jedem Aufruf von fonts.googleapis.com und fonts.gstatic.com – zwei fremde Verbindungen hintereinander, und genau dort arbeitet Umut am meisten. Beide Vorschauen nutzen jetzt dieselben lokalen Dateien wie die öffentliche Seite.

Die Regel steht jetzt im Code, nicht in der Einstellung. page.html.twig hat keinen Google-Zweig mehr; fehlen die Schriftdateien, zeigt die Website die Ersatzschrift des Themes. Die Kopfzeilen lassen keinen fremden Schrifthost mehr zu – öffentlich font-src 'self', in der Verwaltung style-src 'self'; font-src 'self' statt der bisherigen Ausnahmen für fonts.googleapis.com/fonts.gstatic.com. Theme::googleFontsUrl() ist entfernt; das Herunterladen der Dateien über die Theme-Seite bleibt unverändert und baut seine Adresse selbst. Die Vorabholen-Verweise stehen jetzt vor dem Stylesheet, und die Texte auf der Theme-Seite sagen nicht mehr „kommen zurzeit von Google“, sondern „die Dateien fehlen, bis dahin die Ersatzschrift“.

Geprüft (Chromium, kalter Zwischenspeicher, isolierte Kopie der Pilotdatenbank): alle drei Schriftdateien starten bei 96 ms und sind bei 166–168 ms fertig, der erste sichtbare Inhalt erscheint bei 292 ms – die Schriften stehen also vor dem ersten Zeichnen, es gibt nichts mehr zu tauschen. Jede Datei wird genau einmal angefragt. Über die ganze Sitzung – öffentliche Seite, Anmeldung, Seiteneditor mit Vorschau, Bereiche – keine einzige Anfrage außerhalb des eigenen Servers. PHP-Tests: 498, ein bekannter Fehler (siehe unten). npm run test:browser und der Lighthouse-Lauf sind nicht gelaufen.

Tests angepasst: testFontsAreReadFromGoogleStylesheetAndServedFromHere prüft jetzt die Ersatzschrift statt des Google-Verweises und alle drei Vorabhol-Dateien; testThemeDrivesEveryPageAndGoogleFontsOpenTheirOwnPolicy heißt …AndNoFontIsEverFetchedFromGoogle und prüft die engere Kopfzeile. In testEveryAllowedIconHasADrawingAndTakesItsColourFromTheBlock stand eine echte Zeilenschaltung im Suchmuster; unter Windows mit core.autocrlf=true schlägt das fehl, sobald die Arbeitskopie CRLF trägt. Das Muster nimmt jetzt \R und gilt für beide Schreibweisen; die Vorlage selbst bleibt unverändert.

Weiterhin offen, nicht von dieser Aufgabe: CmsTest::testAnyPublishedPageCanBecomeTheRootAndEverythingFollows schlägt fehl – der Test erwartet <a href="/" aria-current="page">Start</a>, das Menü schreibt seit den Symbolen <span class="nav-label"> um die Beschriftung.

Kopfmenü im Hochformat und die Update-Ansicht auf dem Handy (14.09.2026)

Anlass: Umut meldete, das Mobilmenü im Kopf erscheine nur im Querformat, und bat darum, Texte und Ansichten der Update-Seite fürs Handy zu kürzen.

Befund zum Kopfmenü – falsch gebaut, kein Fehler im CMS. Der Kopfbereich trug zwei Menüblöcke: area-header-menu (waagerecht, mobile: display:none) und daneben area-header-burger (eigener Burger-Block, nur mobil sichtbar). Der veröffentlichte Stand auf web.kant-it.de kennt den zweiten Block nicht – abgerufen am 14.09.2026 enthält das Markup dort nur area-header-menu samt @media (max-width:767px){#area-header-menu{display:none}}. Damit ist im Hochformat (bis 767 px) gar kein Menü da, während im Querformat (768–1024 px) das Standardverhalten collapse: tablet denselben Block zum Burger zusammenlegt und ein Knopf erscheint. Genau die gemeldete Beobachtung. In var/kantit.db lag zwar ein zweiter Burger-Block, der das Loch stopfte, aber mit eigener Gestaltung: im Hochformat ein 161 px breiter Knopf, im Querformat der 101 px breite des waagerechten Menüs.

Behoben in var/kantit.db: ein einziger Menüblock area-header-menu mit collapse: tablet und burgerLabel: both, ohne display:none auf Mobil, burgerFontSize: 14px; der doppelte Block area-header-burger ist entfernt (Kopfbereich Fassung 8, Sicherung var/kantit.db.vor-kopfmenue-20260914-2244.bak). Geprüft am laufenden Nachbau: 375 px Hochformat und 844 px Querformat zeigen denselben Knopf („MENÜ“, 101 × 45 px, 14 px), die Liste bleibt in beiden verborgen und die aufklappende Fläche liegt über die volle Breite unter dem Kopf; ab 1025 px stehen die vier Einträge wieder waagerecht. Startkonfiguration kantit (Port 8130) in .claude/launch.json ergänzt.

Update-Ansicht (templates/admin/system.html.twig, public/assets/admin.css). Die Seite schob sich auf dem Handy seitlich aus dem Bild: .stack ist ein Raster mit automatischer Spalte, und eine automatische Rasterspalte wächst auf die Mindestbreite ihres breitesten Inhalts – eine lange Datenbankpfadangabe reichte. .stack bekommt minmax(0, 1fr), .facts beide Spalten als minmax(0, …) und unter 620 px nur noch eine, .card-body und die Prüfpunkte min-width: 0 samt overflow-wrap: anywhere. Die Sicherungstabelle wird unter 620 px zu gestapelten Blöcken (.table-stack, Spaltenüberschrift als data-label), statt waagerecht zu scrollen. Texte gekürzt und das Kleingedruckte in zugeklappte <details class="hint"> gelegt, ebenso die älteren 15 der 20 Protokollzeilen.

Geprüft: 390 × 844 mit Touch-Emulation gegen eine isolierte Kopie der Pilotdatenbank (tests/pilot-fixture.php, eigenes Zufallskonto): Dokumentbreite 390 statt 492, kein Element mehr breiter als der Inhaltsbereich, Wortzahl der Seite 1017 → 556, axe ohne Verstöße. /admin, /admin/media, /admin/menus, /admin/forms, /admin/theme, /admin/settings bleiben mobil bei 390 px; dieselben Seiten und /admin/system bei 1440 px unverändert breit. Screenshots: reports/system-mobile/.

Bekannter Fehler, nicht von dieser Aufgabe: CmsTest::testAnyPublishedPageCanBecomeTheRootAndEverythingFollows schlägt fehl (498 Tests, 1 Fehler) – der Test erwartet <a href="/" aria-current="page">Start</a>, das Menü schreibt seit den Symbolen <span class="nav-label"> um die Beschriftung. Auch ohne diese Änderungen und mit beiden PHP-Fassungen reproduzierbar.

Nächster Schritt: Umut spielt var/kantit.db auf den Server; erst dort ist das Kopfmenü im Hochformat live. Für die Admin-Änderungen die VERSION beim nächsten Release erhöhen, sonst liefert der Browser admin.css?v=1.13.0 weiter aus dem Zwischenspeicher.

1.14.0 – Öffnungszeiten, Einsatzgebiet und FAQ in den strukturierten Daten; Elektro Bernhard aktualisiert (14.09.2026)

Anlass: Umut nannte die tatsächlichen Bürozeiten (Mo–Fr 7:30–17:00), das Einsatzgebiet von Karlsruhe bis Offenburg und wollte die FAQ „mit allem, was notwendig ist“ – das JSON-LD, das das CMS bisher nicht hergab (siehe Lücke in den Einträgen vom Abgleich).

Umgesetzt (ADR 095): Einstellungen openingHours und areaServed mit Formular unter Unternehmensdaten; SiteSettings::organisation() gibt openingHoursSpecification (gleiche Zeiten zusammengefasst) und areaServed aus. Akkordeon-Eigenschaft faq (Schema v23, Kästchen im Editor); PageController baut daraus eine FAQPage mit Frage und Antwort aus dem sichtbaren Inhalt. Fünf neue Tests: Zusammenfassung und Reihenfolge der Zeiten, Abweisung unvollständiger oder verdrehter Zeiten, FAQPage nur aus gekennzeichneten Akkordeons und nur mit Antwort, Abweisung der Kennzeichnung vor v23, und – nach einem Fund der vollen Suite – dass ein Bildschirm, der die geladenen Einstellungen unverändert zurückgibt (Anlegen der Rechtsseiten, Installer), Öffnungszeiten und Einsatzgebiet nicht löscht.

Elektro Bernhard: Bürozeiten überall Mo–Fr 07:30–17:00 Uhr (Fuß, Kontakt, Beschreibung, FAQ); Einsatzgebiet Baden-Baden, Sinzheim, Hügelsheim, Iffezheim, Bühl, Rastatt, Kuppenheim, Muggensturm, Gaggenau, Gernsbach, Murgtal, Ettlingen, Karlsruhe, Achern, Oberkirch, Offenburg in Text und areaServed; FAQ um „Wann ist das Büro erreichbar?“ und „Wie läuft eine Anfrage ab?“ ergänzt und als FAQ gekennzeichnet. Die Öffnungszeiten weichen bewusst von der laufenden Website ab (dort Mo–Do 7:00–16:30, Fr bis 13:00) – Umuts Angabe gilt.

Arbeitsweise: Weil eine parallele Sitzung im Hauptarbeitsbaum dreizehn Dateien offen hatte, darunter Validator, PageController und Editor, entstand diese Änderung im eigenen Worktree var/wt-faq auf dem Zweig oeffnungszeiten-faq und wurde von dort auf main gebracht. Der Hauptarbeitsbaum muss danach git pull ausführen.

1.13.0 – Drei Symbole mehr, gestaltbarer Menü-Knopf, Nachbau „Tagespflege Moldovan“ (14.09.2026)

Anlass: Umut ließ die ZIP-Vorlage „Tagespflege Moldovan“ nachbauen. Beim Nachbau fehlten dem Symbolsatz genau die drei Bilder, die die Vorlage zeigt; danach kamen zwei Meldungen vom Telefon: das Menü sei nicht zu sehen, und „Scroll to top“ funktioniere nicht.

Nachbau (scripts/seed-tagespflege-moldovan.php, scripts/reset-tagespflege-moldovan.sh). Der Onepager aus dem Paket vom 09.09.2026, gebaut aus gewöhnlichen Bausteinen: Kopfleiste mit Telefonzeile, klebender weißer Kopf mit Wortmarke, Hero mit der limettenen Schnittfläche hinter dem Foto (clip-path als eigenes CSS auf einer Fläche), Notizkarte und Bildunterschrift, Werteband, Willkommen mit Porträt-Signatur, drei Angebotskarten, Zeitleiste mit Verbindungslinie aus Flächen, dunkler Kontaktbereich mit schräger Fläche, Formularkarte und Fuß mit limettenem Abschluss. Farben, Schriftgrößen, Abstände und Umbrüche sind aus style.css übernommen, ebenso die drei Gerätestufen. Eigene Installation: var/tagespflege-moldovan.db, var/tagespflege-moldovan-media, Bildquellen in var/tagespflege-moldovan-assets, Startkonfiguration auf Port 8089. Zwei bewusste Abweichungen: das Formular verschickt serverseitig statt eine Mailto-Nachricht vorzubereiten, und das Markenzeichen liegt als PNG vor, weil die Medienverwaltung JPEG, PNG und WebP nimmt. Impressum und Datenschutz fehlen wie in der Vorlage – für einen Livegang nachzutragen.

Symbole (ADR 094). people, music und home ergänzen den Satz in DocumentValidator::ICONS, blocks/icon.html.twig, editor.js und menu-editor.js. Ein Test hält Liste und Zeichnungen zusammen. .block-icon .block-icon{color:inherit} lässt die am Block eingestellte Farbe endlich an der Zeichnung ankommen – das eingebettete SVG trug dieselbe Klasse wie seine Hülle und setzte die Akzentfarbe erneut.

Menü-Knopf (ADR 094). Neue Eigenschaft burgerLabel (both, text, icon; „Nur Symbol“ behält den Namen als visually-hidden) und die neue Stilgruppe menu mit dem Ziel burger: burgerColor, burgerBackgroundColor, burgerBorderColor, burgerBorderWidth, burgerBorderRadius, burgerFontSize. Der Compiler schreibt #id .menu-burger>summary{…}. Im Kopfbereich klappt das Menü als Fläche über die volle Breite unter den Kopf auf statt inline in der schmalen Spalte neben der Marke – das war die gemeldete „unsichtbare“ Navigation.

Nach-oben-Knopf (ADR 094). Die Einblendung hängt nicht mehr an animation-timeline: scroll(), sondern an den Klassen kant-top-armed und kant-top-shown, die scrolled.js setzt; das Skript wird jetzt auch geladen, wenn nur dieser Knopf es braucht. Ohne Skript bleibt der Knopf sichtbar.

Geprüft: PHP-Suite 498 Tests (fünf neue: Symbolliste gegen Zeichnungen, drei Knopfvarianten, abgewiesene Variante, Knopffarben am summary); einziger Fehlschlag ist der bekannte Altfehler testAnyPublishedPageCanBecomeTheRoot…. 33 JavaScript-Tests, Twig-Lint, PHP-Stilprüfung sauber, Editor-Browserprüfung vollständig erfolgreich einschließlich axe. Playwright auf dem Nachbau bei 1440, 900 und 390 px sowie als iPhone 13: Seite gegen die Vorlage verglichen, Menü öffnet als Fläche unter dem Kopf, Sprungziele, 404-Seite, keine Konsolenfehler. Nach-oben-Knopf auf der Elektro-Bernhard-Installation als iPhone geprüft: oben verborgen, ab der Schwelle sichtbar, springt an den Anfang.

Veröffentlicht: Commit 73719ba und Tag v1.13.0 auf origin; Release 1.13.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.13.0), vendor/ ohne Entwicklungspakete; die einzige .env im Archiv ist die Vorlage ohne Geheimnis. Die drei Anhänge wurden anonym von der Releaseadresse geladen und bytegenau mit den gebauten Dateien verglichen; SHA-256 14e333e0…59dcbf, identisch mit der Prüfsumme, die install.php mitbringt.

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.

Veröffentlicht: Commit 042aef9 und Tag v1.12.1 auf origin; Release 1.12.1 mit ZIP (3546 Dateien, 4,6 MB), SHA-256-Datei und install.php, gebaut aus dem sauberen Worktree var/release-source-1.12.1; alle drei Anhänge anonym geladen und bytegenau verglichen, SHA-256 c7a2de81…8f1a23.

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.

Soziale Profile (Dokumentschema v23, ADR 093). Unter Einstellungen → Unternehmensdaten gibt es je Netzwerk ein Adressfeld (SocialProfiles::NETWORKS: Facebook, Instagram, LinkedIn, YouTube, TikTok, X, Threads, Pinterest, Xing, WhatsApp, Google-Unternehmensprofil; nur HTTPS). Der neue Baustein social (Werkzeugkasten → Bausteine → „Soziale Profile“) zeigt sie und bestimmt Größe (die 15 Symbolstufen), Form (nur Symbol, runde oder eckige Fläche), Anordnung, Beschriftung, neuen Tab und optional eine Teilmenge der Netzwerke; Farbe, Hover-Farbe, Abstand und Ausrichtung kommen aus den freien Blockstilen. Die Vorlage liest die Profile über das Twig-Global siteSettings, ohne Profile gibt sie nichts aus und zeigt im Editor einen Hinweis. Die Adressen stehen automatisch in sameAs der strukturierten Daten. Symbole sind eigene Umrisszeichnungen im Strich des Symbolsatzes. Neue Dateien src/Content/SocialProfiles.php, templates/blocks/social-icon.html.twig; Änderungen an Validator, Einstellungen, Controller, Vorlagen, site.css, Editor (Modell, Eigenschaften, Bibliothekssymbol).

Zur Frage nach dem transparenten Menü: Nein, im ersten Nachbau war der Kopf weiß. Jetzt nutzt die Website den Mechanismus aus 1.11.1: headerOverlay und stickyHeader an, Kopfsektion mit #ffffff00 als Hintergrund und Weiß mit Schatten scrolled. Das Logo (schwarze Schrift, transparenter Grund) sitzt auf einer weißen Fläche, damit es über dem dunklen Foto lesbar bleibt.

Elektro Bernhard neu gestaltet (scripts/seed-elektro-bernhard.php, vollständig neu geschrieben): dunkler Hero mit dem Teamfoto als Hintergrund und Überlagerung, Lato aus dem lokalen Schriftordner, Versal-Kleinzeilen in Rot, Leistungskarten mit Symbolfläche, Schatten und Hover-Lift, Foto mit starkem Schatten neben Text und vier Stärken, Kundenstimmen als Slider mit sechs echten Rezensionen (Punkte, Automatik 7 s), dunkles Solarband mit dunkler Karte, Akkordeon in weichen Kästen, Formular als weiße Karte, jede Unterseite mit dunklem Seitenkopf unter dem schwebenden Kopfbereich, Handlungsaufruf als dunkle Karte, Einblendungen mit gestaffelten Verzögerungen auf allen Abschnitten. Soziale Profile in der Kopfleiste (16 px, nur Symbol, auf dem Telefon ausgeblendet) und im Fuß (20 px, runde Fläche, Hover rot). „Nach oben“-Knopf in Rot. Keine Änderung am CMS-Code außer dem Baustein.

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.

Veröffentlicht: Commit fd4cb75 und Tag v1.12.0 auf origin; Release 1.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)

Auftrag: ZIP-Vorlage exakt nachbauen, eigene Datenbank und Medien; ausdrücklich keine CMS-Codeänderung, Inhalte und Gestaltung ausschließlich über die vorhandene Editoroberfläche bearbeitbar aufbauen.

Vorlage gefunden unter C:/Users/UmutKantarci/AppData/Local/Temp/codex-file-preview-oZxj9Z/Tagespflege_Moldovan_Website.zip, sicher nach var/tagespflege-moldovan-source entpackt. Enthält einen Onepager, CSS/JS, zwei Fotos und marke.svg; die unbenutzte Datei Schmetterling_Moldovan.png ist leer. Formular bereitet nur eine Mailto-Nachricht vor, Rechtstexte fehlen in der Vorlage.

Angelegt: var/tagespflege-moldovan.db, alle 15 Migrationen erfolgreich; separates var/tagespflege-moldovan-media; Startkonfiguration tagespflege-moldovan auf Port 8089. Über die Installationsoberfläche Website-Grundgerüst angelegt. Bestehende .env nach den Installeränderungen bytegleich aus der vorher angelegten Sicherung wiederhergestellt; die neue Installation verwendet Prozessvariablen. Keine Änderungen am CMS-Code durch diese Aufgabe.

Noch offen: Erstes Administratorkonto im geöffneten Browser unter http://127.0.0.1:8089/install/konto fertigstellen und anmelden. Automatische Freigabeprüfung wies zuvor den kombinierten Shellbefehl für Kontoanlage und Serverstart ab (keine genauere Begründung); der verworfene Setuphelfer wurde entfernt und nicht ausgeführt. Server separat erfolgreich gestartet. Danach den eigentlichen Nachbau vollständig im Editor durchführen, Medien hochladen, Desktop/Mobil vergleichen und Speichern/Neuladen prüfen. Noch kein Vorlagennachbau und keine visuelle Abnahme.

1.11.4 – Slider-Steuerung auf dem Bild wie bei kant-it.de (14.09.2026)

Anlass: Umut wollte nicht die Knopfleiste unter dem Slider, sondern das Design des Hero-Sliders auf www.kant-it.de (eigenes Plugin, kein Flatsome-Slider): runde weiße Pfeile auf dem Bild, Punkte unten rechts mit aktivem Strich in der Akzentfarbe, keine Zählerleiste, Pfeile auf dem Handy aus.

Umgesetzt: slider.js legt Slider und Steuerung in eine .slider-shell (beim Aufräumen wieder entpackt) und benennt Pfeile und Pause (slider-prev/next/play, data-playing); site.css zeichnet die Steuerung als Ebene über dem Bild: Pfeile 50 px rund mit Chevron, Punkte 9 px (aktiv 28 px Strich), Pause 32 px unten rechts, Zähler nur noch für Vorlesesoftware, Pfeile unter 600 px ausgeblendet. Neue Gestaltungseigenschaft „Aktiver Punkt“ (sliderDotActiveColor, --slider-dot-active). Der Compiler-Selektor #id + .slider-controls gilt in der Shell weiterhin.

Geprüft: Playwright auf der Pilotkopie bei 1600 und 390 px: Pfeile mittig links/rechts (Desktop) bzw. ausgeblendet (Mobil), Punkte und Pause unten rechts innerhalb des Bildes, Screenshots reports/editor/slider-design-*.png visuell geprüft. Zwei gezielte PHP-Tests, 33 JavaScript-Tests, Editor-Browserprüfung vollständig erfolgreich.

Veröffentlicht: Commit cef710d und Tag v1.11.4 auf origin; Release 1.11.4. Weil parallel die Arbeit an „Soziale Profile“ (1.12.0) im selben Arbeitsbaum lief, wurden nur die eigenen Hunks committed und das Archiv (3544 Dateien, 4,6 MB) aus einem sauberen Worktree des Tags gebaut; Anhänge anonym heruntergeladen und bytegenau verglichen, SHA-256 53904dea…9a663b.

Elektro Bernhard: Abgleich mit dem bestehenden Auftritt, SEO nachgezogen (14.09.2026)

Anlass: Umut bat darum, den Nachbau gegen die laufende Website elektro-bernhard.de zu halten und SEO nachzuholen.

Dabei zwei Falschangaben gefunden und korrigiert. Das gelieferte HTML-Paket ordnete zwei Google-Rezensionen falsch zu: „Super Firma und einfach tolle Mitarbeiter“ stammt von Redzepi Samir (26.10.2024), nicht von Philipp Bender; Philipp Bender (26.11.2024) hat die Architekten-Rezension geschrieben. Der dritte Eintrag des Pakets war eine selbst formulierte Zusammenfassung statt der tatsächlichen Worte. Die Zuordnung ist am DOM der Live-Seite geprüft (.wprevpro_t1_DIV_1: erst Text, dann Name, dann Datum), nicht aus der flachen Textfassung geraten. Jetzt stehen drei Rezensionen im Originalwortlaut mit richtigem Namen und Datum; der Hinweis darunter nennt den Stand und sagt ausdrücklich, dass kein Gesamtdurchschnitt ausgewiesen wird.

Aus dem bestehenden Auftritt nachgeholt: die sechs Stärken („Kundenzufriedenheit“, „Qualität“, „Erfahrung“, „Flexibel“, „Professionell“, „Schnell & sauber“), die das Paket zu drei Absätzen Fließtext zusammengezogen hatte – jetzt als Symbolkarten auf der Leistungsübersicht, die dadurch von 8 auf 23 Bausteine wächst. Dazu der Handlungsaufruf „Projekt geplant? Wir sind bereit.“ als cta-Baustein auf der Übersicht und auf allen vier Leistungsseiten. Das Einsatzgebiet nennt jetzt auch Kuppenheim, Gaggenau, Gernsbach und Muggensturm – Orte, die der bestehende Auftritt in seinen eigenen Keywords führt.

SEO: sameAs mit den drei Profilen, die die Live-Seite selbst verlinkt (Google-Unternehmensprofil, Facebook, Instagram) – sie stehen jetzt in den strukturierten Daten. Unternehmensbeschreibung auf Leistungen, Notdienstnummer und Bürozeiten erweitert; sie ist zugleich die Zusammenfassungszeile in llms.txt. Vorschaubild beim Teilen gesetzt (Startseite Teamfoto, Leistungsseiten Installationsmotiv), womit og:image und twitter:card: summary_large_image ausgeliefert werden statt einer Textkarte. Meta-Beschreibungen liegen zwischen 110 und 159 Zeichen.

llms.txt gab es schon seit dem Aufbau (Einstellung llmsTxt); es listet alle zehn indexierbaren Seiten mit Beschreibung.

Bewusst nicht per Skript gesetzt: der Auszug (excerpt) je Seite. Das Feld erscheint in der Verwaltung nur bei Beiträgen; ein per SQL geschriebener Wert wäre unsichtbar und würde beim nächsten Speichern der Seitendaten stillschweigend gelöscht. llms.txt nimmt ersatzweise die Seitenbeschreibung, und die ist im Editor bearbeitbar.

Offene Lücke gegenüber dem HTML-Paket: dessen JSON-LD enthielt openingHoursSpecification, areaServed, einen contactPoint für den Notdienst sowie FAQPage und Service. Das CMS kennt diese Felder nicht – es liefert LocalBusiness (Name, Adresse, Telefon, E-Mail, sameAs) und BreadcrumbList. Das nachzurüsten ist eine Kernerweiterung (Einstellungen, SiteSettings::organisation(), FAQ aus Akkordeons ableiten) mit eigenem Release, nicht eine Änderung an dieser Website. Noch nicht umgesetzt, nicht beauftragt.

Geprüft: Alle zehn Adressen 200, /gibtsnicht 404, llms.txt und sitemap.xml ausgeliefert; strukturierte Daten, og:image und twitter:card im Quelltext kontrolliert; Leistungsübersicht, Leistungsseite und Kundenstimmen in der App-Vorschau angesehen; keine Konsolenfehler. Das größte Dokument (Startseite) liegt bei 117 Bausteinen und 27 KiB – Grenze sind 300 und 256 KiB.

1.11.3 – Farben der Slider-Steuerung (14.09.2026)

Anlass: Umut will die Punktfarben am Slider einstellen; auf dem dunklen Hero waren die Standardfarben (dunkle Punkte, weiße Knöpfe) nicht anpassbar.

Umgesetzt: Drei Gestaltungseigenschaften in der neuen Gruppe „Slider-Steuerung“ (sliderDotColor, sliderControlColor, sliderControlBackground) mit dem Ziel controls: der Compiler schreibt sie als --slider-dot, --slider-ink, --slider-bg auf den Slider und auf die vom Skript dahinter eingefügte Steuerleiste (#id,#id + .slider-controls{…}); site.css liest die Variablen für Punkte, Pfeile, Zähler, Knopfhintergrund und die skriptfreien ::scroll-marker. Die Gruppe erscheint nur an Slidern (relevantProperty).

Geprüft: Neuer Test testSliderControlsTakeTheirColoursFromTheSliderStyle (Ausgabe mit Hex- und Palettenfarbe). Playwright im Editor der Pilotkopie: Gruppe am Hero-Slider vorhanden, Punktfarbe #ff0000 gesetzt, Regel in der Vorschau angekommen, Punkt bei „Bewegung abspielen“ rot gezeichnet. 33 JavaScript-Tests, Editor-Browserprüfung vollständig erfolgreich.

Veröffentlicht: Commit 6dc30a4 und Tag v1.11.3 auf origin; Release 1.11.3 mit ZIP (3548 Dateien), SHA-256-Datei und install.php als neuestes stabiles Release; Anhänge anonym heruntergeladen und bytegenau verglichen, SHA-256 a9536289…f45a24. Der Commit enthält auch den parallel entstandenen Statuseintrag zur Elektro-Bernhard-Referenzwebsite; deren Skripte sind noch nicht committed.

Zweite Referenzwebsite: Elektro Bernhard in eigener Datenbank (14.09.2026)

Anlass: Umut hat das fertige statische Paket Elektro-Bernhard-Website (HTML/CSS/JS, erstellt 12.09.2026) geliefert mit dem Auftrag, es vollständig im CMS nachzubauen – eigene Datenbank, eigener Medienordner, im Editor bearbeitbar.

Umgesetzt: Neue Installation neben dem Kant-IT-Piloten, nach demselben Muster: Datenbank var/elektro-bernhard.db (alle 15 Migrationen), Medien in var/elektro-bernhard-media, Startkonfiguration elektro-bernhard auf Port 8088 in .claude/launch.json. Aufgebaut wird sie von scripts/seed-elektro-bernhard.php (Neuaufbau mit scripts/reset-elektro-bernhard.sh), damit der Bestand reproduzierbar ist und nicht von Hand zusammengeklickt werden muss.

Inhalt: elf Seiten (Startseite, Leistungsübersicht mit vier Leistungsseiten, Kontakt, Solarenergie, Impressum, Datenschutz, 404), Kopf- und Fußbereich als gemeinsame Bereiche, vier Menüs (Haupt, Notdienst, Fuß, Rechtliches), Kontaktformular mit sechs Feldern, drei Bilder aus dem Original in der Mediathek, Palette und Schrift der Vorlage im Design, drei Weiterleitungen. Alles besteht aus vorhandenen Bausteinen – Abschnitt, Zeile, Spalte, Überschrift, Text, Bild, Symbolkarte, Kennzahl, Zitat, Akkordeon, Kontakt, Formular, Brotkrumen, Menü, Rechtszeile, Trennlinie. Kein neuer Bausteintyp, keine Änderung am CMS-Code.

Drei Stellen weichen bewusst ab, weil das Schema sie so verlangt:

  • /leistungen/… braucht im CMS eine übergeordnete Seite; die Vorlage hatte nur den Anker /#service. Es gibt deshalb zusätzlich eine Leistungsübersicht unter /leistungen, und /service leitet dorthin.
  • Eine Weiterleitung zeigt auf einen Pfad, nicht auf ein Sprungziel: /ueber-uns landet auf der Startseite, die den Abschnitt trägt, statt auf /#ueber-uns.
  • Ein Button verlinkt nur lokale Pfade, Sprungziele und HTTPS. Die Telefonnummern der Vorlage sind deshalb Kontakt-Bausteine (mit tel:-Verweis), gestaltet wie der zweite Button der Vorlage.

Für drei Feinheiten, die die Gestaltungsfelder nicht erreichen (Symbolfarbe in der Karte, Sternfarbe im Zitat, Pluszeichen im Akkordeon), steht je zwei Zeilen unter „Eigenes CSS“ am Baustein – im Editor sichtbar und änderbar.

Geprüft: Alle elf Adressen antworten mit 200, /gibtsnicht mit der eigenen 404-Seite, die drei Weiterleitungen mit 301 auf das erwartete Ziel; sitemap.xml, robots.txt und llms.txt werden ausgeliefert. In der App-Vorschau Startseite von oben bis unten mit der Vorlage verglichen (Hero, Leistungen, Über uns mit Kennzahlen, Marken, Kundenstimmen, Solar, Fragen, Kontakt, Notdienststreifen, Fuß), dazu Leistungsseite und Impressum; auf 375 px Kopfleiste ohne Adresse und Hamburger-Menü mit den vier Originaleinträgen. Im Editor ist die Startseite geladen, der Strukturbaum zeigt den vollständigen Baum, ein Textbaustein lässt sich auswählen und über das Eigenschaftenfeld bearbeiten. Keine Konsolenfehler auf der öffentlichen Seite.

Nicht geprüft: Kein Formularversand (keine Mailzustellung lokal), keine Lighthouse-Messung, kein Livegang. Die Rechtstexte stammen aus der Vorlage; im Datenschutz ist der Abschnitt zum Formular auf den Serverversand des CMS umgeschrieben und als „NOCH ZU PRÜFEN“ markiert, weil Hoster und Aufbewahrungsdauer offen sind.

1.11.2 – Slider stottert nicht mehr auf dem iPhone (14.09.2026)

Anlass: Umuts iPhone-Screenshot des Hero-Sliders („das buggt“). Safari bricht scrollTo({behavior:'smooth'}) in einem Container mit scroll-snap-type: x mandatory ab und rastet auf die vorige Folie zurück; Pfeile und Automatik ruckeln.

Umgesetzt: slider.js schaltet das Einrasten für die Fahrt aus und stellt es bei Ankunft am Zielwert wieder her (spätestens nach zwei Sekunden, weil ein Hintergrund-Tab sanftes Scrollen pausiert); bei reduzierter Bewegung weiter sofortiges Springen. Die Punkte der Steuerung sind gezeichnete Punkte (aktiv gefüllt) statt eines Kastens um den Text, Trefffläche bleibt 44 px.

Geprüft: Playwright auf der Pilotkopie bei 375 und 1600 px: „Nächste Folie“ landet exakt auf dem Folienanfang (359 bzw. 666 px), Einrasten danach wieder x mandatory, Automatik blättert nach 7 s selbst weiter; 33 JavaScript-Tests. Der Safari-Fall selbst ist hier nicht ausführbar; die Maßnahme ist die dokumentierte Umgehung dieses WebKit-Verhaltens. Editor-Browserprüfung vollständig erfolgreich.

Veröffentlicht: Commit 769b0fa und Tag v1.11.2 auf origin; Release 1.11.2 mit ZIP (3549 Dateien), SHA-256-Datei und install.php als neuestes stabiles Release; Anhänge anonym heruntergeladen und bytegenau verglichen, SHA-256 548e1bf0…ba4a07.

1.11.1 – Kopfbereich mit Scroll-Farben, gleich hohe Karten (14.09.2026)

Anlass: Umut sah auf der Startseite ungleich hohe Karten und wünschte sich für den Kopfbereich einstellbare Transparenz-Effekte beim Scrollen, wie in Flatsome.

Umgesetzt: (1) .block-iconcard + .block-iconcard und die verwandten Stapelabstände gelten in .block-row nicht mehr; die Karten in einer Zeile sind gleich hoch. (2) Neuer Zustand scrolled neben hover in der Gestaltung: StyleSchema::scrollable() (Hintergrund-, Text-, Rahmen-, Schattenfarbe, Deckkraft), Prüfung im StyleCompiler, Ausgabe als html.is-scrolled #id{…} mit Übergang; Design-Panel zeigt „… beim Scrollen“ nur im Kopfbereich; scrolled.js markiert die Seite ab 40 px, wird nur geladen, wenn der Kopfbereich Scroll-Werte hat (Seiten und Archive, CSP entsprechend); die Editor-Vorschau setzt die Klasse selbst. Neue Einstellung „Kopfbereich über dem Inhalt schweben lassen“ (headerOverlay, Klasse is-overlay, absolut bzw. fixiert mit „oben halten“). ADR 092.

Geprüft: Neuer Test testAHeaderCanChangeItsColoursOnceThePageHasScrolled (Ausgabe, Skript, CSP, Ablehnung unzulässiger Scroll-Werte); PHP-Suite 485 Tests grün bis auf den bekannten Altfehler. In der App-Vorschau: sechs Leistungskarten 227 px, Kundenstimmen 232 px, „Warum“-Karten 191 px, gestapelte Karten behalten 18 px. Scroll-Farbe im Kopfbereich-Editor über das Farbfeld gesetzt, in der Vorschau als Regel angekommen und gespeichert. 33 JavaScript-Tests, Twig-Lint aller 49 Vorlagen, PHP-Stilprüfung. Editor-Browserprüfung vollständig erfolgreich, einschließlich aller axe-Prüfungen.

Veröffentlicht: Commit 3785d3d und Tag v1.11.1 auf origin; Release 1.11.1 mit ZIP (3547 Dateien, 4,7 MB), SHA-256-Datei und install.php als neuestes stabiles Release angelegt. Anhänge anonym heruntergeladen und bytegenau mit dem lokalen Build verglichen; SHA-256 970a12bc…6ff306.

Kant-IT-Startseite: Einblendungen nach Flatsome-Muster in var/kantit.db (14.09.2026)

Umgesetzt: Auf Umuts Wunsch das Animationsmuster der alten Flatsome-Website (www.kant-it.de: fadeInLeft/Right im Hero, bei „Warum“ und Kontakt, gestaffeltes fadeInUp auf Zahlen, Karten und Fußzeilenspalten) in der lokalen Kant-IT-Datenbank nachgebaut: 55 Blöcke der Startseite umgestellt – Sektionen ohne eigene Einblendung, Hero-Text von links, Slider-Spalte von rechts, Kennzahlen und Karten gestaffelt von unten, „Warum“-Texte und -Karten von links, Kundenstimmen von rechts, Kontaktdaten links und Formular rechts – sowie die vier Fußzeilenspalten gestaffelt von unten. Als Entwurf gespeichert und veröffentlicht (Revision 155e2934…), Sicherung var/kantit.db.vor-bewegung-*.bak. Umut kopiert die Datei selbst auf den Server; web.kant-it.de läuft bereits auf 1.11.0 (Fassung, motion.js, Hover und Einblendungen live geprüft).

Geprüft: scripts/motion-check.mjs (Ziel jetzt #abschluss-cta, weil die Sektion #kontakt keine eigene Einblendung mehr trägt) auf frischer Pilotkopie bei 1600/390 px erfolgreich: 45 Einblendungen gerendert (30 von unten, 11 von links, 4 von rechts), Warten außerhalb des Bildes, einmalige Wiedergabe, Richtungen, Hover, Standardmodus unter „reduce“, motion-respect. Keine JavaScript-Fehler in der App-Vorschau.

1.11.0 – Editor mit einer Seitenleiste nach Flatsome-Vorbild, Bewegung spielt standardmäßig (14.09.2026)

Anlass: Umut fand den Editor mit drei Panels unübersichtlich und kam mit dem Flatsome UX Builder zurecht; außerdem liefen die Einblendungen in seinem Chrome nicht, weil der Browser „Bewegung reduzieren“ meldet, obwohl er das nicht eingestellt hat – andere Websites bewegen sich bei ihm.

Umgesetzt: Editor-Gerüst neu (templates/admin/editor.html.twig, editor.css, editor.js): dunkle Kopfzeile mit Gerätewahl in der Mitte, eine Seitenleiste mit den Reitern Elemente/Struktur/Seite und einer Elementansicht (Name, Pfad, Inhalt/Design/Erweitert, Aktionen unten, ‹ zum Elternelement, + zum Einfügen, ✕ zum Schließen), Elementkarten im Raster, Kontextmenü per Rechtsklick in der Vorschau, Escape/Entf-Tasten auch bei Fokus in der Vorschau, „Bearbeiten“ neben dem gewählten Strukturknoten. Panelbreiten und der Werkzeugkasten-Umschalter entfallen. Sichtbarkeit am Ende von Inhalt und Erweitert. Bewegung: Website-Einstellung motion (always Standard, respect), Klasse motion-respect auf <html>, motion.js versteckt wartende Blöcke und blendet sie von unsichtbar ein, zeigt übersprungene Blöcke sofort; Hover-CSS ohne Medienschranke, Überschreibblock nur für motion-respect; slider.js folgt derselben Regel. ADR 091.

Geprüft: scripts/motion-check.mjs auf isolierter Pilotkopie (1600/390 px) erfolgreich: Warten außerhalb des Bildes, Start beim Scrollen, einmalige Wiedergabe, Richtungen, gespeicherter Hover, Standard spielt unter emuliertem „reduce“, motion-respect unterdrückt und nimmt wieder auf, Editorhinweis nur bei motion-respect. In der App-Vorschau mit realen Kant-IT-Daten: Auswahl, Design-Reiter, Kontextmenü, +-Einfügen (Zeile → nur Spalte erlaubt, Spalte → Überschrift), Entf, Strg+Z, Tablet-Ansicht, öffentliche Startseite mit 14 Einblendungen (7 warten, alle sichtbar nach Sprung ans Seitenende), keine JavaScript-Fehler. 33 JavaScript-Tests, Twig-Lint der geänderten Vorlagen, angepasster CSS-Test in CmsTest. scripts/editor-check.mjs auf die eine Seitenleiste umgestellt (Reiter-Helfer, Bearbeiten im Baum, Flatsome-Modus in der Sandbox) und vollständig erfolgreich, einschließlich aller axe-Prüfungen (Desktop- und Mobil-Editor, Mediathek, Verwaltungsseiten), Entwurf/Veröffentlichung, Konflikt und Einwilligung.

Bekannt / Grenzen: CmsTest::testAnyPublishedPageCanBecomeTheRootAndEverythingFollows schlägt bereits ohne diese Änderungen fehl (Menülink Start fehlt) – nicht Teil dieses Schritts. Kein neuer Lighthouse-Lauf. Die Ursache der Browsermeldung in Umuts Chrome bleibt offen; die Website hängt nicht mehr davon ab.

Veröffentlicht: Commit a1f8b90 und annotierter Tag v1.11.0 auf origin; Release 1.11.0 mit ZIP (3549 Dateien, 4,7 MB), SHA-256-Datei und install.php als neuestes stabiles Release über die API angelegt. Alle drei Anhänge anonym unter releases/download/v1.11.0/ heruntergeladen und bytegenau mit dem lokalen Build verglichen; SHA-256 49338e77…63ca56 stimmt mit der Datei und dem Installer überein. Die lokale Startkonfiguration .claude/launch.json für die Pilotkopie ist nicht committed.

1.10.2 – Fehler 500 beim Abschluss des Archiv-Updateaufrufs (14.09.2026)

Reproduziert und korrigiert: Echter Produktionsmodus mit den vollständigen Paketen 1.10.0 → 1.10.1: Update-POST HTTP 500 nach erfolgreichem Dateitausch, fehlende PHP-Dienste im gerade gelöschten Containercache. Systemseite anschließend HTTP 200, Migrationsabschluss HTTP 302, öffentliche Seite danach HTTP 200. Ursache und Übergang: ADR 090. Der Kernel hält nun seinen Cachepfad pro Anfrage fest und verwendet für jede Releasefassung ein eigenes Verzeichnis. Der Archiv-Updater entfernt keine Dateien, die die laufende Anfrage noch benötigt.

Geprüft: 28 PHP-Tests / 347 Assertions für Archiv- und Git-Updater, Cachepfad, Systemverwaltung und Installation erfolgreich. Wiederverwendbarer Paket-HTTP-Test reproduziert 1.10.0 → 1.10.1 mit 500 sowie den erfolgreichen Wiederaufnahmeweg (var/archive-check-f00fc15412). Mit dem echten Zielpaket 1.10.2: Übergang aus 1.10.1 weiterhin einmalig POST 500, danach System 200, Abschluss 302, öffentlich 200 (var/archive-check-316de9113a). Korrigierter Paket-Updater mit ausschließlich synthetischer vorheriger Versionsnummer: POST 302, System 200, Abschluss 302, öffentlich 200, keine fehlenden Cachedienste (var/archive-check-8637d9310e). Sitzung, Wartungszustand, Sicherung, Versionswechsel und unveränderte Originaldatenbank geprüft. Produktionscontainer, Cache-Warmup, PHP-Stil und JavaScript-Syntax erfolgreich.

Veröffentlicht: Commit 7c1c86b, Tag v1.10.2, Release 1.10.2 auf origin und als neuestes stabiles Release geprüft. Paket aus dem exakten Commit, 3543 Archivdateien und 221 Quelldateien bytegenau geprüft; nur Produktionsabhängigkeiten. Frischer Installer HTTP 200, Container und Cache-Warmup auch im Release erfolgreich. ZIP, Prüfsumme und Installer anonym heruntergeladen und mit den lokalen Artefakten verglichen. ZIP-SHA-256: 733f591c143e9d98d4044f37dabce93086542e4db2512b1d4f051617d8835711.

Übergabe / Grenzen: Der bereits installierte alte Updater kann beim Einspielen dieser Reparatur einmalig weiter 500 ausgeben; anschließend /admin/system direkt öffnen und die angebotenen Migrationen abschließen. Genau dieser Weg ist mit dem Releasepaket geprüft. Kein Zugriff auf Produktionsprotokolle und keine Produktionsänderung durch diese Diagnose; Startseite und Anmeldung bei der Prüfung HTTP 200. Kein neuer Lighthouse- oder visueller Gesamtlauf. Die während der Arbeit extern geänderte .claude/launch.json bleibt unberührt und uncommitted.

1.10.1 – Standardbutton im Formular und zuverlässiges Wiederaufnehmen von Einblendungen (14.09.2026)

Umgesetzt: „Absenden“ verwendet den konfigurierten Website-Standardbutton, einschließlich Form, Darstellung, Größe, Schrift sowie normalen und Hover-Farben. Harte Formularfarben entfernt und natürliche Buttonbreite hergestellt. Bestehende Formulare übernehmen Theme-Änderungen ohne Inhaltsmigration (ADR 089). Enthalten sind außerdem die unten dokumentierte Observer-Korrektur und die dynamische Bewegungsmeldung im Editor.

Prüfungen: Formularabweichung vor der Korrektur mit realen berechneten CSS-Werten reproduziert (var/form-button-before.log). 10 gezielte PHP-Tests / 76 Assertions für Formulare, Validierung, Versand, Spam-Schutz und Menüformular erfolgreich; eine alte Menüerwartung auf die seit 1.10.0 getrennten responsiven Darstellungen angepasst. 33 JavaScript-Tests, alle 49 Twig-Dateien, Produktionscontainer und PHP-Stilprüfung erfolgreich. Bewegungstest auf isolierter Pilotkopie bei 1600/390 px einschließlich Präferenzwechsel und Editorhinweis erneut erfolgreich. Kein neuer Lighthouse-Gesamtlauf oder Test des normalen Chrome-Benutzerprofils.

Browser: Drei Button-Prüfgruppen erfolgreich auf isolierter Kant-IT-Kopie. Globale Vererbung des Formularbuttons bei 1600/900/390 px ohne JavaScript mit berechneten CSS-Werten gegen einen gewöhnlichen Standardbutton verglichen; Hover, Tastaturfokus, natürliche Breite sowie spätere Umstellung von Rahmen/Pille auf gefüllt/abgerundet/groß mit eigenen Farben geprüft. Bestehende Button-Tests für Undo/Redo, Gerätevarianten und Veröffentlichung ebenfalls erfolgreich. Screenshots unter reports/buttons/ visuell geprüft.

Veröffentlicht: Commit cff4681 und Tag v1.10.1 auf origin; Release 1.10.1 als neuestes stabiles Release öffentlich geprüft. ZIP, SHA-256-Datei und Installer anonym heruntergeladen und bytegenau verglichen. Paket aus dem exakten Commit: 3543 Archivdateien, 221 Paketquelldateien verglichen, ausschließlich Produktionsabhängigkeiten. Frischer Installer HTTP 200, .env vorhanden und keine .env.local; Produktionscontainer und Cache-Warmup im Release erfolgreich. ZIP-SHA-256: 24733774d14fe1c640681d01e62d2cef2b6a337b24b63af416965ba63cf4654c.

Nächster Schritt / Grenzen: Über die Systemverwaltung aktualisieren und den Editor neu laden. Die Browservorgabe prefers-reduced-motion bleibt wirksam; ein dauerhaftes true wird durch dieses Release nicht überschrieben. Keine Kundeninhalte oder Systemeinstellungen verändert; auf web.kant-it.de hier kein Update installiert.

Einblendungen nach Freigabe wieder aufnehmen – lokal korrigiert (14.09.2026)

Neue Evidenz: Umuts Screenshot zeigt eingeschaltete Windows-Animationseffekte. Auch die lokale Win32-Abfrage SPI_GETCLIENTAREAANIMATION ergibt true; kein Chrome-Prozess mit --force-prefers-reduced-motion gefunden. Die frühere Empfehlung, Windows-Animationen einzuschalten, ist damit nicht als Lösung belegt. Warum Umuts Chrome-Profil dennoch prefers-reduced-motion: reduce meldet, bleibt offen; eine Chrome-DevTools-Medienemulation ist eine mögliche, nicht verifizierte Ursache.

CMS-Fehler reproduziert und behoben: Der Observer in motion.js startete nach dem Wechsel von reduzierter zu erlaubter Bewegung nicht erneut. Der neue Browserfall scheiterte vor der Änderung (var/motion-resume-before.log). Jetzt werden noch nicht gezeigte Elemente wieder beobachtet; bereits gezeigte Elemente laufen weiterhin nur einmal, laufende Animationen werden bei Reduktion abgebrochen. Keine Änderung an der grundsätzlichen Beachtung der Browserpräferenz. Im Editor erscheint eine dynamische Statusmeldung, wenn der Browser Einblendungen, Hover-Bewegungen und Slider-Automatik unterdrückt.

Geprüft: Erweiterte scripts/motion-check.mjs auf isolierter Pilotkopie bei 1600/390 px erfolgreich: Start unter Reduktion und anschließende Freigabe ohne Reload, nachträgliches Sperren/Freigeben, keine Wiederholung bereits gezeigter Elemente, Hover aus/an, alle bisherigen Scroll-/Richtungsfälle, lesbare Ausgabe ohne JavaScript. Editorhinweis erscheint/verschwindet bei Präferenzänderung, Screenshot reports/motion/editor-motion-status.png visuell geprüft. 33 JavaScript-Tests, Twig-Lint für den Editor und Git-Diff-Prüfung erfolgreich; Originaldatenbank unverändert.

Direkte Produktionsgegenprobe: Mit installiertem Chrome in zwei getrennten Testkontexten auf web.kant-it.de: bei emuliertem reduce keine Einblendung und Hover-Transform none; bei no-preference Kontaktsektion mit translateY(28px) und App-Karte beim Hover um −4 px verschoben. Keine JavaScript-Seitenfehler; Bericht reports/motion/production-proof.json. Während der Prüfung war an leistung-app inzwischen fx-entrance-none gespeichert; keine Produktionsinhalte verändert. Dieser Vergleich zeigt die Wirkung der Präferenz, ersetzt jedoch keine Prüfung des normalen Benutzerprofils.

Offen / nächster Schritt: Die lokale Observer-Korrektur erklärt keinen dauerhaft wahren Browserwert und behebt bei weiter aktivem reduce nicht Umuts gesamten Produktionsbefund. Chrome-Rendering-Emulation prüfen; Windows-Einstellungen nicht erneut als Ursache behaupten. Lokale Änderungen noch nicht committed oder veröffentlicht, keine neue Datenmigration, kein neuer Lighthouse-Gesamtlauf.

Produktionsdiagnose: Bewegung in Chrome unterdrückt (14.09.2026)

Bestätigte Ursache: Umut meldet für https://web.kant-it.de/ in seinem Chrome matchMedia('(prefers-reduced-motion: reduce)').matches === true. Damit deaktivieren die vorhandenen CSS-/JavaScript-Regeln Einblendungen, Hover-Bewegungen und Slider-Automatik. Die öffentliche Karte leistung-app trägt tatsächlich fx-hover-lift fx-entrance-left fx-delay-short; die frühere Diagnose allein anhand der lokalen Pilotdaten war für Produktion unzureichend.

Geprüft: Produktions-HTML und Effektdateien HTTP 200; motion.js?v=1.10.0 entspricht dem lokalen Modul, ausgeliefertes CSS enthält die Hover-Regel samt Bewegungsschranke. Im Codex-Browser ebenfalls reduzierte Bewegung und deaktivierte Slider-Automatik gemessen, keine JavaScript-Warnungen/-Fehler. Direkter Zugriff auf Umuts Chrome wurde von der Browsersteuerung wegen nicht sicher ermittelbarer URL gestoppt; den Wert aus seinem Browser hat Umut selbst bestätigt.

Testverbesserung: scripts/motion-check.mjs prüft zusätzlich den gespeicherten Hover-Effekt anhand der berechneten Verschiebung von −4 px und protokolliert die Bewegungsvorgabe separat ohne Medienemulation (reducedMotion: null). Optional installiertes Chrome über MOTION_BROWSER_CHANNEL=chrome; dieser Lauf auf einer isolierten Pilotkopie war bei 1600/390 px erfolgreich. Ein frisches Testprofil ersetzt weiterhin keine Messung im Benutzerprofil.

Einordnung: Der später bereitgestellte Screenshot und die Systemabfrage zeigen, dass Windows-Animationen bereits eingeschaltet sind (siehe neuerer Eintrag oben). Die Herkunft der Browsermeldung bleibt ungeklärt; keine erfolgreiche Wiedergabe im normalen Chrome-Profil bestätigt. Keine Systemoption, Produktionsdatei oder gespeicherter Inhalt geändert; kein neues Release.

Prüfung der Scroll-Einblendungen mit Kant-IT-Daten (14.09.2026)

Befund: Die Startseite in var/kantit.db enthält in Entwurf und Veröffentlichung 14 Einblendungen, ausschließlich rise und fade. Keine seitlichen Effekte an den Startseitenelementen gespeichert. rise bewegt den Block 28 Pixel bei voller Deckkraft; überwiegend sind ganze Sektionen animiert. Der bestehende Renderer lädt motion.js, und der Observer löst beim Scrollen aus. Im Editor erfordert die Wiedergabe „Bewegung testen“; reduzierte Systembewegung unterdrückt sie.

Geprüft: PHP_BINARY=C:\php84\php.exe node scripts/motion-check.mjs (PowerShell: zuerst $env:PHP_BINARY='C:\php84\php.exe') erfolgreich auf isolierter Pilotkopie im Produktionsmodus, Chromium mit Desktop 1600 und Mobil 390. Kontaktsektion wartet außerhalb des Bildschirms, startet nach Scrollen und läuft nur einmal. Beide seitlichen Optionen auf demselben Element temporär im Browser geprüft, inklusive tatsächlich berechneter Transformationsrichtung. Reduzierte Bewegung und sichtbarer Inhalt ohne JavaScript erfolgreich; keine JavaScript-Seitenfehler. Screenshots visuell geprüft, Bericht unter reports/motion/result.json. SHA-256 der Originaldatenbank vor/nach identisch.

Ergebnis / nächster Schritt: Kein Ausfall der Scroll-Laufzeit reproduziert; Anwendung und gespeicherte Inhalte unverändert. Für seitliche Bewegung die gewünschten Blöcke unter Design → Bewegung und Effekte → Einblendung beim Erscheinen auf „Von links“/„Von rechts“ stellen, speichern und veröffentlichen. Ob Umuts Browser aktuell Bewegung reduziert, wurde nicht geprüft. Keine neue Lighthouse- oder vollständige Barrierefreiheitsprüfung.

1.10.0 – Gerätesichtbarkeit und automatische Hamburger-Menüs (14.09.2026)

Erledigt: Eigene Sichtbarkeitsauswahl direkt am Element in Inhalt/Design/Erweitert: alle Geräte, nur Desktop/Tablet/Mobil, auf einem Gerät ausblenden oder überall ausblenden. Strukturbaum zeigt die Auswahl und erhält den Zugriff auf ausgeblendete Elemente. Waagerechte Menüs werden standardmäßig auf Tablet/Mobil und großen Touchgeräten ohne Maus zum Hamburger. Pro Menü sind „Nur Mobil“ und „Nie“ möglich. Vertikale und ausdrücklich gewählte Burger-Menüs behalten ihre Menüart. Native Menübedienung ohne öffentliches JavaScript; Öffnen auch in der Editorvorschau möglich. Tabs und Slider berücksichtigen ausgeblendete Einträge. Datenvertrag und Rückweg: ADR 088, Schema 22, keine SQL-Migration.

Geprüft: PHP_BINARY=C:\php84\php.exe npm run test:responsive-navigation (in PowerShell mit $env:PHP_BINARY setzen) erfolgreich mit vier Browser-Prüfgruppen auf isolierter Kant-IT-Kopie: Tastatur, Undo/Redo, Rücksetzen, Strukturzugriff, Vorschau, Speichern/Laden/Veröffentlichung; alle acht Sichtbarkeitsoptionen bei 1600/1025/1024/768/767/390 px; geerbte Elternsichtbarkeit, erhaltene Flex-/Gridwerte; alle Menüvarianten ohne JavaScript einschließlich Tastatur und Linknavigation; 1366-px-Touchprofil; Tabs/Slider und gezielte WCAG-Audits für Navigation, Tabs und Slidersteuerung auf Desktop/Mobil. Screenshots unter reports/responsive-navigation/ visuell geprüft. Kein Testkonto oder Inhalt in den Originaldaten verändert. Der erste Start mit PHP 8.2 im PATH war wegen dessen fehlender SQLite3-Erweiterung nicht möglich; der dokumentierte PHP-8.4-Lauf ist erfolgreich.

Weitere Prüfungen: 33 JavaScript-Tests; 2 neue PHP-Tests mit 30 Assertions; 50 gezielte bestehende PHP-Tests zu Menüs, Dokument-Roundtrip und ungültigen Dokumenten mit 86 Assertions. PHP-Formatierung, alle 49 Twig-Dateien, Produktionscontainer und Cache-Warmup erfolgreich. Bestehende HTML-Erwartungen berücksichtigen jetzt beide responsiven Menüvarianten; der Browser prüft, dass jeweils nur eine zugänglich ist. Ein im Browser gefundener CSS-Fehler bei ausgeblendeten Slider-Punkten wurde korrigiert.

Release-Prüfung 1.10.0: 33 JavaScript-Tests und 139 gezielte PHP-Tests / 234 Assertions erfolgreich. Eine alte HTML-Erwartung im Test für gemeinsame Header/Footer an die vorhandene Menübeschriftung angepasst. Responsive-Navigationsprüfung mit vier Gruppen und Scroll-Prüfung bei 1600/390 px auf isolierten Pilotkopien erneut erfolgreich. Alle 49 Twig-Dateien, Produktionscontainer und PHP-Stilprüfung der drei betroffenen PHP-Dateien ohne Befund.

Veröffentlicht: Commit 37c2942 und Tag v1.10.0 auf origin; Release 1.10.0 als neuestes stabiles Release öffentlich geprüft. ZIP, SHA-256-Datei und Installer anonym heruntergeladen und bytegenau verglichen. Paket aus dem exakten Commit: 3543 Archivdateien, 221 Paketquelldateien verglichen, ausschließlich Produktionsabhängigkeiten. Frische Installationsseite HTTP 200, .env vorhanden und keine .env.local; Produktionscontainer und Cache-Warmup im Release erfolgreich. ZIP-SHA-256: beee5723eb27a2de58b4fc32c66a521a1469d7ded00046a25ddac51a34cc86ba. Ergebnisse unter var/release-verification-1.10.0.json.

Bekannte Grenzen / nächster Schritt: Kein neuer Lighthouse-Gesamtlauf, keine neue Feldmessung, kein Test auf physischem iPad. Geräteklassen richten sich nach Viewportbreite; die Menüautomatik berücksichtigt zusätzlich Touchbedienung. Bereits separat angelegte Desktop-/Mobilmenüs können mit der neuen Sichtbarkeit zugeordnet werden. Nach dem Release über die Systemverwaltung aktualisieren und die gewünschten Sichtbarkeiten im echten Header festlegen. Kundenhostings werden hier nicht verändert.

1.9.0 – Button-Vorlagen, Sprungziele und bessere Menüs (14.09.2026)

Erledigt: Website-Standardbutton unter Typografie mit acht visuellen Vorlagen, normaler und Hover-Vorschau, Form, Darstellung, Größe, Schrift und Farben. Einzelne Buttons erhalten Gerätevarianten und Rücksetzen über den bestehenden Undo-/Speicherweg. Ein vorhandener Fehler beim Speichern leerer Palettenzeilen wurde behoben. Sprungziele wie #kontakt lassen sich an Sektionen, Zeilen und Spalten vergeben; interne IDs bleiben stabil und doppelte Ziele werden abgewiesen. Menüs haben sichtbare Drag-and-drop-Griffe, Tastaturbedienung, ordentliche SVG-Pfeile sowie optionale Icons vor/nach dem Text. Neben der Fassung erscheint ein Update-Link aus dem Ergebnis der bestehenden täglichen Prüfung.

Gezielt geprüft: Je drei Browser-Prüfgruppen für Buttons und Navigation auf isolierter Kant-IT-Kopie erfolgreich: Vorlagen und globale Speicherung, Gerätevarianten, Undo/Redo, Speichern/Laden, Veröffentlichung, Sprunglinks, Menü-Drag-and-drop/Tastatur, Icons und Update-Hinweis. Öffentliche Ausgabe ohne JavaScript geprüft; Screenshots unter reports/buttons/ und reports/navigation-ux/ visuell kontrolliert. Originaldaten unverändert. Twig-Lint und Produktionscontainer erfolgreich. Im gezielten PHP-Lauf zu Theme/Menüs bestanden 29 von 30 Tests unmittelbar; eine alte HTML-Erwartung wurde an die neue Icon-Beschriftung angepasst und der betroffene Test erfolgreich wiederholt (8 Assertions). PHP-Formatierung abgeschlossen. Auf Wunsch keine Gesamtprüfung und keine neue Lighthouse-Messung.

Veröffentlicht: Commit 3391fea und Tag v1.9.0 auf origin; Release 1.9.0 ist das neueste stabile Release. ZIP, SHA-256-Datei und Installer öffentlich heruntergeladen und bytegenau verglichen. Paket aus dem exakten Commit: 3543 Archivdateien, 221 Paketquelldateien verglichen, ausschließlich Produktionsabhängigkeiten. Frische Installationsseite HTTP 200, .env vorhanden und keine .env.local. ZIP-SHA-256: f48f135015f1ab2fb49a6f0a862d1ef414d20a8ca61a70d43e85c9b0e100bf62.

Grenzen / nächster Schritt: Über die Systemverwaltung aktualisieren und den Editor neu öffnen. Update-Hinweis setzt den vorhandenen Aufgaben-Worker voraus und erscheint nach dessen Prüfung. Freie CSS-/Designwerte können Button-Vorlagen übersteuern. Schema 21 ist beim Rückweg zu berücksichtigen (ADR 087); keine Datenbankmigration. Kundenhostings wurden hier nicht verändert.

1.8.4 – Bildausrichtung sichtbar korrigiert (14.09.2026)

Behoben: „Eigene Ausrichtung quer: Mitte“ zentriert Bilder jetzt auch in normalen Sektionen und Spalten. Zuvor blieb das Bild trotz align-self:center links; der Browserfall mit 50-Prozent-Breite zeigte 181 Pixel Abweichung von der Mitte (var/image-alignment-before.log, reports/image-alignment/before.png). Der gemeinsame Compiler richtet Rahmen und enthaltenes Bild aus, einschließlich kleiner Logos, Prozent-/Maximalbreiten und Bildlinks. Anfang/Ende, Gerätevererbung und Rücksetzen sind berücksichtigt; horizontale Flex-Gruppen behalten ihre vertikale Querachse. Keine Datenmigration, keine Änderung an gespeicherten Bilddaten, kein zusätzliches öffentliches Skript (ADR 086).

Browser geprüft: npm run test:image-alignment mit drei Prüfgruppen auf isolierter Kant-IT-Kopie erfolgreich: echte Layout-Auswahl, Tastatur, Anfang/Mitte/Ende, Undo/Redo, gepolsterte Inhaltsbereiche, kleine Originalbilder, Prozent-/Maximalbreiten, Link/Vergrößerung, Grid, beide Flexachsen, responsive Elterneinstellungen und Rücksetzen auf Auto mit wiederhergestelltem Außenabstand. Speichern/Laden erhält Maße und Medienmetadaten. Öffentliche Ausgabe ohne JavaScript auf Desktop 1600, Tablet 900 und Mobil 390 geprüft. Screenshots unter reports/image-alignment/ visuell geprüft; Originaldaten unverändert.

Zusätzlich geprüft: Bestehendes npm run test:image-size mit drei Prüfgruppen erfolgreich, einschließlich Größenregler, Gerätewerten, Undo/Redo, Speichern/Laden, Veröffentlichung und Desktop-/Mobil-Barrierefreiheit der Bedienelemente. 32 JavaScript-Tests und Produktionscontainer erfolgreich; PHP-Stilprüfung der drei betroffenen Dateien ohne Befund. Alle 481 PHP-Tests / 3865 Assertions erfolgreich, verteilt auf sämtliche 15 Testklassen mit jeweils eigenem Prozess (php -d max_execution_time=0 vendor/bin/phpunit tests/<Klasse>Test.php). Ergebnis: var/phpunit-1.8.4-result.json, Einzellogs unter var/phpunit-1.8.4-classes/. Der Aufgaben-Worker setzt im gemeinsamen Prozess erneut ein 80-Sekunden-Limit; deshalb wurde die Suite pro Klasse ausgeführt. Ein vorhandener CRLF/LF-abhängiger Quelltextvergleich der Layout-Gruppen ist jetzt zeilenumbruchunabhängig. Kein Anwendungscode wurde für die Testlaufzeit geändert.

Veröffentlicht: Commit 81276dd und Tag v1.8.4 auf origin; Release 1.8.4 ist das neueste stabile Release. ZIP, SHA-256-Datei und install.php öffentlich heruntergeladen und bytegenau verglichen. Paket aus dem exakten Commit: 3535 Archivdateien, 213 Paketquelldateien gegen Commit/Vorlage verglichen, nur Produktionsabhängigkeiten. Archivintegrität, Installer-Prüfsumme, Produktionscontainer/Cache und frische Installationsseite (HTTP 200, .env, keine .env.local) erfolgreich. ZIP-SHA-256: 4e7b429381cadcc5d9ed857b03917c3abab0197c37fb47e570d03bf50d69eea4.

Nächster Schritt / Grenzen: Über die Systemverwaltung aktualisieren und den Editor neu öffnen; Kundenhostings wurden hier nicht verändert. Keine neue Lighthouse-Messung oder vollständige Editor-Gesamtprüfung für die gezielte CSS-Compiler-Korrektur; eigene CSS-Regeln können weiterhin Layoutvorgaben überschreiben.

1.8.3 – Inhalte in Akkordeons sichtbar bearbeiten (14.09.2026)

Behoben: Geschlossene Akkordeon-Einträge ließen sich in der Editorvorschau nicht öffnen, weil die Klickbehandlung die native Aktion verhinderte. Der Fehler wurde vor der Korrektur im Browser reproduziert (var/accordion-before.log, reports/accordion/before.png). Einträge sind im Editor jetzt geöffnet, leere Inhaltsbereiche sichtbar und als Ablageziel nutzbar. „Inhalt hinzufügen“ im Eintrag und rechts im Feinschliff hängt neue Elemente an. „Eintrag hinzufügen“ steht an der Gruppe und in deren Inhaltsansicht; auch ein vollständig leeres Akkordeon ist wieder befüllbar.

Bedienung: Rechte Eintragsübersicht, Struktur und Struktursuche zeigen Eintragstitel. Ein neues Akkordeon wählt den ersten Eintrag samt Inhaltsoptionen. Bei Auswahl der Gruppe werden Texte nicht mehr unbemerkt außerhalb des Akkordeons eingefügt. Die gespeicherte Einstellung „Beim Laden geöffnet“ gilt weiterhin für Besucher; Öffnen im Editor verändert weder Daten noch Speicherstatus. Keine Migration und kein geänderter öffentlicher Renderer (ADR 085).

Geprüft: npm run test:accordion mit fünf Prüfgruppen auf isolierter Kant-IT-Kopie: sichtbarer Inhalt trotz geschlossenem Besucherzustand, Einfügen über Tastatur/Vorschau/Feinschliff, Textbearbeitung, Anhängen und Drag-and-drop von Buttons in Einträge, Undo/Redo, weitere/leere/verschachtelte Akkordeons, Suche nach Titeln, Speichern/Laden und Veröffentlichung. Veröffentlichte Einträge behalten ihre Öffnungswerte und lassen sich ohne JavaScript per Maus/Enter bedienen; Vorschauhilfen fehlen im öffentlichen HTML und in gespeicherten Daten. Desktop 1600/Mobil 390 ohne axe-Verstöße im Editor oder horizontalen Überlauf; Screenshots visuell geprüft (reports/accordion/). Originaldaten unverändert.

Zusätzlich geprüft: Vollständiges npm run test:editor erfolgreich, einschließlich Speichern/Veröffentlichen, Konflikten, Medien, Einwilligung und Desktop-/Mobil-Barrierefreiheit. 32 JavaScript-Tests erfolgreich, darunter ergänzte Einfüge-/Undo-Prüfungen. 16 gezielte PHP-Tests / 33 Assertions zu V6-Blockvalidierung, Speichern und Ausgabe ohne JavaScript erfolgreich. Produktionscontainer gültig und Git-Diff ohne Whitespacefehler. Keine erneute PHP-Vollsuite oder Lighthouse-Messung für die Editoränderung.

Veröffentlicht: Commit a302996 und Tag v1.8.3 auf origin; Release 1.8.3 ist das neueste stabile Release. ZIP, SHA-256-Datei und install.php öffentlich heruntergeladen und bytegenau verglichen. Paket aus dem exakten Commit: 3535 Archivdateien, 212 versionierte Quelldateien verglichen, nur Produktionsabhängigkeiten. Archivintegrität, Installer-Prüfsumme, Produktionscontainer/Cache und frische Installationsseite (HTTP 200, .env, keine .env.local) erfolgreich. ZIP-SHA-256: 7a3625609dc55f00354ee4b853c2816f20467cd04d0aa9acde7d117c69bd897a.

Nächster Schritt: Über die Systemverwaltung aktualisieren und den Editor neu öffnen; Kundenhostings wurden hier nicht verändert.

1.8.2 – Visuelle Farbauswahl im Editor (14.09.2026)

Umgesetzt: Kompakte Farbvorschau mit Palettenname und aufgelöstem Hexwert statt schwarzem Feld bei @deep und einer zusätzlichen Dropdown-Liste. Aufklappbare Website-Farben als beschriftete Farbkacheln mit Auswahlzustand, eigener Farbwähler und Hexeingabe mit Enter/Übernehmen. Transparente Farben auf Schachbrett; Alphakanal bleibt beim Ändern des Farbtons erhalten. Geerbte Geräte- und Hover-Farben erkennbar, einzelne Werte zurücksetzbar. Text-, Hintergrund-, Hover-, Verlaufs-, Rahmen- und Schattenfarben nutzen dasselbe Bedienelement. Keine Änderung am Datenvertrag oder öffentlichen Paket (ADR 084).

Geprüft: npm run test:colors mit fünf Prüfgruppen auf isolierter Kant-IT-Kopie: Token-Vorschau, Auswahl/Kanvas, Tastatur/Tab/Escape/Fokus, Undo/Redo, Geräte-/Hover-Vererbung und Reset, stabiler nativer Farbwähler mit einem Undo-Schritt, reine change-Ereignisse, ungültige Eingaben, Kurzhex, achtstellige Hexwerte, Speichern/Laden und Veröffentlichung mit erhaltener Palettenreferenz. Desktop 1600/Mobil 390: geöffnete Farbauswahl und Editor ohne axe-Verstöße oder horizontalen Überlauf; Screenshots reports/color-picker/ visuell geprüft. Originaldaten unverändert.

Zusätzlich geprüft: Vollständiges npm run test:editor erfolgreich, einschließlich Gestaltung, Vorlagen, Speichern/Veröffentlichen, Konflikten, Mediathek, Einwilligung sowie Desktop-/Mobil-Barrierefreiheit. Der bisherige Test zur automatisch angehängten Einwilligungszeile wurde an die bereits veröffentlichte Änderung aus 1.8.1 angepasst. 32 JavaScript-Tests einschließlich versionierter Modulimporte bestanden. Geänderte Twig-Basis und Produktionscontainer gültig; Git-Diff ohne Whitespacefehler.

Veröffentlicht: Commit 55dab4b und Tag v1.8.2 auf origin; Release 1.8.2 ist das neueste stabile Release. ZIP, SHA-256-Datei und install.php öffentlich heruntergeladen und bytegenau verglichen. Paket aus dem exakten Commit: 3535 Archivdateien, 212 versionierte Quelldateien verglichen, nur Produktionsabhängigkeiten. Archivintegrität, Installer-Prüfsumme, Produktionscontainer/Cache und frische Installationsseite (HTTP 200, .env, keine .env.local) erfolgreich. ZIP-SHA-256: f6d2a6ff99f52f3a0939160e26403aca13bb9fb9d7042f38255dd958057ae8f5.

Grenzen / nächster Schritt: Keine erneute PHP-Vollsuite oder Lighthouse-Messung für die Änderung am Editor. Verwaltungsformulare außerhalb des Editors behalten ihre bisherigen Farbfelder. Über die Systemverwaltung aktualisieren und den Editor neu öffnen; Kundenhostings wurden hier nicht verändert.

Behoben: Das CMS hängt Datenschutz, Impressum und Einwilligungslink nicht mehr automatisch an den Footer an. Entfernt aus Seiten-/Archiv-/Fehlervorlagen und damit auch aus Seiten- und Bereichsvorschauen. Normale Textlinks, Menüs und ausdrücklich eingefügte „Rechtliches“-Bausteine bleiben erhalten; der Editorhinweis beschreibt die freie Platzierung. Keine Inhaltsänderung oder Migration (ADR 083).

Geprüft: PHP-Vollsuite 477 Tests / 3815 Assertions und 32 JavaScript-Tests erfolgreich. Vorhandene Tests an die redaktionelle Platzierung angepasst; freiwilliger Rechtsblock und Einwilligungsablauf weiterhin geprüft. 47 Twig-Dateien und Produktionscontainer gültig, PHP-Stilprüfung der drei betroffenen PHP-Dateien ohne Änderungen, Git-Diff ohne Whitespacefehler.

Browser: Isolierte Kant-IT-Kopie mit bestehenden Rechtsblöcken sowie einer Variante mit normalen Footer-Textlinks wie im Screenshot. Acht Prüfgruppen erfolgreich: Startseite, Beitragsarchiv und 404 auf Desktop 1600/Mobil 390 sowie Seiten- und Footer-Editor. Eigene Links erhalten, keine Zusatzleiste, kein horizontaler Überlauf. Screenshots unter reports/footer-links/ visuell geprüft. Gegenprobe mit dem unveränderten Release 1.8.0 reproduziert die doppelte Leiste auf allen sechs öffentlichen Ansichten (reports/footer-links-before/). Originaldaten bleiben unverändert.

Bekannte Prüfbefunde: Die Variante mit gewöhnlichen, unformatierten Textlinks meldet Kontrastbefunde an diesen Links; bei sichtbarem Nach-oben-Knopf zusätzlich einen Landmark-Befund. Das Archiv meldet außerdem Überschriftenreihenfolge und doppelte Landmark-Benennung. Alle Befunde betreffen dieselben Elemente wie in der Gegenprobe mit 1.8.0. Keine fehlerfreie Gesamt-Barrierefreiheit oder neuen Lighthouse-Werte behauptet. Keine Wiederholung der gesamten Editor-Bedienprüfung für diese Template-Entfernung.

Veröffentlicht: Commit 8f475d1 und Tag v1.8.1 auf origin; Release 1.8.1 ist das neueste stabile Release. ZIP, SHA-256-Datei und install.php öffentlich heruntergeladen und bytegenau verglichen. Paket aus dem exakten Commit: 3534 Archivdateien, 211 versionierte Quelldateien verglichen, nur Produktionsabhängigkeiten. Archivintegrität, Installer-Prüfsumme, Produktionscontainer/Cache und frische Installationsseite (HTTP 200, .env, keine .env.local) erfolgreich. ZIP-SHA-256: fc5d7386d1d9b099e1506f16ab7937526014a85a96840c510e7ca4e2703206c3. Über die Systemverwaltung aktualisieren; Kundenhostings wurden hier nicht verändert.

1.8.0 – Eigene 404-Seite mit Website-Design (14.09.2026)

Umgesetzt: Einstellungen → 404-Seite mit Livesuche, bestehende Seite zuweisen, Auswahl zurücksetzen, direktem Bearbeiten und öffentlicher 404-Vorschau. „404-Vorlage anlegen“ erstellt und verknüpft einen bearbeitbaren Entwurf, ohne vorhandene Seiten zu überschreiben. Bis zur Veröffentlichung erscheint eine automatische Standard-404. Normaler Seitenrenderer übernimmt Standard-Header/-Footer, Theme, Schriften, Menüs und verknüpfte Sektionen. Die ID-Verknüpfung übersteht Umbenennen; Löschen führt auf den Standard zurück. Nur veröffentlichte Revisionen werden öffentlich ausgeliefert.

Antwortverhalten: Echte 404-Antwort an der ursprünglichen URL, auch bei ungewöhnlichen/tiefen HTML-Adressen und beim direkten Aufruf der zugewiesenen Seite. Kein Canonical und keine strukturierten Daten einer normalen Seite; noindex, follow in HTML und Header. Ausschluss der zugewiesenen ID aus Sitemap und llms.txt. 301/410, Verwaltungs-/Medien-/Formularendpunkte und maschinenlesbare Ressourcen behalten ihre eigene Behandlung. Zuweisung mit Veröffentlichungsrechten, CSRF und Schutz gegen veraltete Formulare. Keine Datenbankmigration; optionaler Settings-Schlüssel und vorhandenes Dokumentschema v10 (ADR 082).

Geprüft: PHP-Vollsuite 477 Tests / 3817 Assertions erfolgreich, einschließlich neuer 404-Fälle zu Entwurf/Veröffentlichung, Umbenennen, Löschen/Fallback, bestehenden /404-Seiten, Schreibkonflikten, Berechtigungen, CSRF, HEAD, Bereichen, 301/410, Sitemap und llms.txt. 32 JavaScript-Tests erfolgreich. npm run test:404 mit isolierter Kant-IT-Kopie: Vorlage anlegen, im Editor bearbeiten, veröffentlichen, tatsächliche Ausgabe, Livesuche/Tastaturwahl, Zuordnung nach Neuladen, Rücksetzen und veraltetes zweites Formular. Desktop 1600 und Mobil 390 ohne Überlauf/axe-Verstöße; öffentliche Startseite weiterhin 200 mit Canonical und ohne axe-Befund. Funktioniert auch ohne JavaScript; Header/Footer/Schrift mit dem Originalpiloten abgeglichen, Screenshots visuell geprüft (reports/not-found/). Originaldaten bleiben unverändert.

Zusätzlich geprüft: Vollständiges npm run test:editor erfolgreich, einschließlich Speichern/Laden, Veröffentlichung, Konflikten, Mediathek und Desktop-/Mobil-Barrierefreiheit. Alle 47 Twig-Dateien und der Produktionscontainer gültig; PHP-Stilprüfung über 133 Dateien ohne Befund.

Veröffentlicht: Commit 16da844 und Tag v1.8.0 auf origin; Release 1.8.0 ist das neueste stabile Release. ZIP, SHA-256-Datei und install.php öffentlich heruntergeladen und bytegenau verglichen. Paket aus dem exakten Commit: 3534 Archivdateien, 211 versionierte Quelldateien gegen den Commit verglichen, nur Produktionsabhängigkeiten. Archivintegrität, Installer-Prüfsumme, Produktionscontainer/Cache und frische Installationsseite (HTTP 200, .env, keine .env.local) erfolgreich. ZIP-SHA-256: 4c90a0256ad827c40ae09db85fbe72a9315f41b181c26f9983d710b646bc64b4.

Grenzen / nächster Schritt: Keine neuen Lighthouse-Scores für eine absichtlich nicht indexierbare 404-Seite. Keine neuen Firefox-/Safari-/Screenreader- oder MySQL-Läufe; lokale Datenbankprüfungen verwenden SQLite. Nach Veröffentlichung über die Systemverwaltung aktualisieren; Kundenhostings werden in diesem Arbeitsschritt nicht verändert.

1.7.4 – URL-Feld bei Bild-Klickaktion zuverlässig einblenden (14.09.2026)

Behoben: „Zu einer Adresse führen“ blendet Linkziel und Tab-Häkchen unmittelbar unter dem Dropdown ein, auch bei Bildern ohne frühere Verlinkung. Inhalts-Auswahlfelder reagieren auf input und change, ohne doppelte Änderungen. Das Dropdown bleibt beim Umschalten im DOM; Linkfelder werden nur ein-/ausgeblendet, eingegebene Adressen bleiben erhalten. Jede Auswahl bleibt einzeln rückgängig zu machen. Keine neue Schema-/Datenbankmigration, kein geänderter öffentlicher Renderer oder öffentliches Paket. Cachekennung 1.7.4.

Geprüft: Der ergänzte Browserfall mit ausschließlich change scheiterte vor dem Fix am fehlenden URL-Feld (var/image-link-before.log). Danach npm run test:link-media mit sechs Prüfgruppen bestanden: erstmals verlinktes Bild, natives Dropdown mit Mausöffnung/Tastaturwahl, Tastaturwechsel, stabiler DOM-Knoten/Fokus, Nur-change-Ereignis, URL-Erhalt, Vergrößern/Nichts tun, Undo/Redo, Speichern/Laden und veröffentlichte Zieladresse. Bestehende Bild-, Hintergrund-, Textlink- und Menüprüfungen ebenfalls erfolgreich. Desktop-/Mobil-Editor und öffentliche Seite ohne axe-Verstöße; Screenshots visuell geprüft (reports/link-media/). 32 JavaScript-Tests bestanden. Schreibtests ausschließlich in isolierten Kant-IT-Kopien.

Zusätzlich geprüft: Vollständiges npm run test:editor erfolgreich, einschließlich Inhalts-/Design-Auswahl, Undo, Speichern, Veröffentlichen, Konflikten, Mediathek und Desktop-/Mobil-Barrierefreiheit (reports/editor/).

Veröffentlicht: Commit 25fd388 und Tag v1.7.4 auf origin; Release 1.7.4 ist das neueste stabile Release. ZIP, SHA-256-Datei und install.php öffentlich heruntergeladen und bytegenau verglichen. Paket aus dem exakten Commit: 3532 Archivdateien, 209 versionierte Quelldateien gegen den Commit verglichen, nur Produktionsabhängigkeiten. Archivintegrität, Installer-Prüfsumme, Produktionscontainer/Cache und frische Installationsseite (HTTP 200, .env, keine .env.local) erfolgreich. ZIP-SHA-256: f41ff10ad403a5dccd48e8d2805079dc3ebdfa2966fd548c4c1b0abe6d26a617.

Grenzen / nächster Schritt: Kein erneuter PHP- oder Lighthouse-Lauf für diese reine Editor-Korrektur. Kundenhostings werden nicht verändert; nach Einspielen des Updates den Editor neu öffnen, damit die versionierten Skripte geladen werden.

Umgesetzt: „In neuem Tab öffnen“ bei Buttons, verlinkten Bildern, Symbolen, Symbolkarten, Aufruf-Bändern, Textlinks und Menüeinträgen. Serverseitig validierte Ausgabe mit target="_blank" rel="noopener"; normale Links bleiben im selben Tab. Textlinks lassen sich mit vorhandenem Ziel/Häkchen wieder bearbeiten. „Nichts tun“ am Bild deaktiviert auch eine früher hinterlegte Verlinkung. Inhalts-, Galerie-/Logo- und Hintergrundbilder zeigen Vorschauen mit Lade-/Fehlerzustand und Entfernen-Funktion. Entfernen ist rückgängig zu machen und erhält die Datei in der Mediathek. Bildaustausch erhält Link, Bildunterschrift und Gestaltung. Geerbte Hintergrundbilder sind sichtbar und je Gerät entfernbar; kleinere Geräte übernehmen die Entfernung, sofern dort kein eigener Wert besteht. Verläufe bleiben erhalten. Tastaturfokus bleibt beim Umschalten der Klickaktion und nach dem Entfernen eines Hintergrunds bedienbar.

Datenvertrag: Dokument-Schema v20 nur beim Verwenden neuer Block-Linkwerte oder ausdrücklicher Hintergrundentfernung. Vorhandene Dokumente bleiben lesbar, keine Datenbankmigration. Migration und Rückweg: ADR 081. Medienvorschauen verwenden eine authentifizierte Einzelabfrage und tatsächlich vorhandene kleine Varianten, auch bei archivierten verwendeten Bildern. Kein zusätzliches öffentliches JavaScript.

Geprüft: 338 PHP-Tests (CmsTest|EditorSecurityTest) mit 2873 Assertions; 32 JavaScript-Tests; 47 Twig-Vorlagen gültig; PHP-Stilprüfung über 131 Dateien ohne Befund. npm run test:link-media auf einer isolierten Kant-IT-Kopie: Vorschauen, fehlendes Bild, Entfernen/Undo bei Bild/Galerie/Hintergrund, Vererbung/Reset, Links erstellen/ändern/deaktivieren, Menü-Undo, Speichern/Laden und Veröffentlichung. Neuer Tab tatsächlich geöffnet, Ursprungstab erhalten und window.opener null. Veröffentlichter Hintergrund ohne JavaScript bei 1600/900/390 px geprüft. Öffentliche Seite sowie Desktop-/Mobil-Editor ohne axe-Verstöße; Screenshots visuell geprüft (reports/link-media/). Vollständiges npm run test:editor ebenfalls erfolgreich, einschließlich Mediathek, Entwurf/Veröffentlichung/Konflikten und Desktop-/Mobil-Barrierefreiheit. Originaldaten bleiben unverändert; Schreibtests ausschließlich in temporären Kopien.

Veröffentlicht: Commit 6d21c45 und Tag v1.7.3 auf origin; Release 1.7.3 ist das neueste stabile Release. ZIP, SHA-256-Datei und install.php anschließend ohne Anmeldung heruntergeladen und mit den lokalen Dateien verglichen. Aus dem exakten Commit gebaut: 3532 Archivdateien, 209 versionierte Quelldateien bytegenau verglichen, nur Produktionsabhängigkeiten. Archivintegrität, Installer-Prüfsumme, Produktionscontainer/Cache und frische Installationsseite (HTTP 200, .env, keine .env.local) erfolgreich. ZIP-SHA-256: acf6a4ebea555fe7613935372e1a7922aca4e2c72646175a93d349359787e315.

Grenzen und nächster Schritt: Keine erneute Lighthouse-Messung für diese Link-/Mediensteuerung; öffentliche Ausgabe und Barrierefreiheit direkt geprüft. Keine neuen Firefox-/Safari-/Screenreader-Prüfungen. Nach Veröffentlichung kann das Update über die Systemverwaltung eingespielt werden; Kundenhostings werden in diesem Arbeitsschritt nicht verändert.

1.7.2 – Bildbreite direkt in Prozent einstellen (14.09.2026)

Umgesetzt: Beim ausgewählten Bild steht im Reiter Inhalt oben „Bildbreite (%)“ mit Regler, Zahlenfeld und Zurücksetzen. 1–100 % beziehen sich auf die verfügbare Breite der Sektion/Spalte. Desktop, Tablet und Mobil verwenden die vorhandene Vererbung; Herkunft und bestehende maximale Breiten sind sichtbar. Beide Bildsteuerungen bearbeiten denselben gespeicherten Breitenwert wie Design → Größe. Originalmaße, Medienvarianten, Alt-Text und Bildunterschrift bleiben erhalten. Ungültige Zahlen verändern den gespeicherten Wert nicht, eine Ziehbewegung bleibt ein Undo-Schritt. Der veraltete Hinweis auf eine erst künftig verfügbare Mediathek wurde berichtigt. Keine neue Schema-/Datenbankmigration, keine Änderung am öffentlichen Renderer oder öffentlichen Paket.

Geprüft: npm run test:image-size auf einer isolierten Kant-IT-Kopie mit dem TeamViewer-Logo: Regler mit echter Mausbewegung und Tastatur, Zahlenfeld, Fokus, ungültiger Wert, Undo/Redo, responsive Vererbung/Reset, Speichern/Laden, unveränderte Bilddaten und Abgleich mit Design → Größe. Veröffentlichte Ausgabe ohne JavaScript bei 1600/900/390 px: 60/75/30 % und unverzerrtes Seitenverhältnis gemessen. Neue Steuerung auf Desktop/Mobil ohne Überlauf und axe-Verstöße, Screenshots visuell geprüft (reports/image-size/). Vollständige Editor-Browserprüfung erfolgreich, einschließlich Speichern/Veröffentlichen, Konflikten, Mediathek und Desktop-/Mobil-Barrierefreiheit (reports/editor/). 30 JavaScript-Tests und 35 PHP-Tests zu Styles/Bildern mit 67 Assertions erfolgreich. Alle Schreibtests in eigenen Datenbankkopien.

Veröffentlicht: Commit 0920e3c und Tag v1.7.2 auf origin; Release 1.7.2 ist das neueste stabile Release. ZIP, SHA-256-Datei und install.php anschließend ohne Anmeldung heruntergeladen und mit den lokalen Dateien verglichen. Aus dem exakten Commit gebaut: 3531 Archivdateien, 208 versionierte Quelldateien bytegenau verglichen, nur Produktionsabhängigkeiten. Archivintegrität, Installer-Prüfsumme, Produktionscontainer/Cache und frische Installationsseite (HTTP 200, .env, keine .env.local) erfolgreich. ZIP-SHA-256: e9983669871815e46c8a675bf59eb3632f37f015c0026ccb072a93119def5ac8.

Grenzen und nächster Schritt: Das Update kann über die Systemverwaltung eingespielt werden; Kundenhostings wurden nicht verändert. Da nur die Editorsteuerung vorhandener Style-Werte geändert wurde, keine erneuten öffentlichen Lighthouse-Messungen; die Prozentdarstellung wurde im gemeinsamen Renderer direkt überprüft.

1.7.1 – Livesuche in der Verwaltung (14.09.2026)

Umgesetzt: Seiten und Beiträge suchen beim Tippen per AJAX; Status, 20/50 Einträge und Pagination aktualisieren nur die Ergebnisliste. URL, Browser-Zurück/Vor, Eingabefokus und Rückkehrfilter beim Speichern bleiben zusammenhängend. Die Mediathek durchsucht Titel und Alternativtext über alle Seiten, kombiniert mit Archivfilter. Bestehende Suchen in Menüs, übergeordneten Seiten, Editor-Werkzeugkasten/Struktur/Eigenschaften und Bildauswahl reagieren bereits beim Tippen. Kein Enter nötig; ohne JavaScript bleibt „Anwenden“ erhalten. Alte Antworten werden verworfen, Fehler erhalten Treffer und Eingaben, Wiederholen ist direkt möglich. Eine während der Suche begonnene Bearbeitung hält den Austausch an. SQLite-Suche erkennt Groß-/Kleinschreibung auch bei Umlauten. Keine Datenmigration und kein zusätzliches öffentliches JavaScript.

Geprüft: PHP 8.4.22: EditorSecurityTest|CmsTest 332 Tests / 2832 Assertions; nach der abschließenden Korrektur zur Suchanfrage „0“ die fünf Livesuche-/Medienfälle erneut mit 111 Assertions. 30 JavaScript-Tests; 24 Admin-Twig-Vorlagen; Produktionscontainer; PHP-Stilprüfung über 131 Dateien ohne Befund. npm run test:search: sechs Browser-Prüfgruppen mit kopierten Kant-IT-Daten, darunter schnelle/überholte Anfragen, Fehler/Wiederholen, abgelaufene Sitzung, IME, Pagination/Historie, Seitenauswahl nach AJAX, Bearbeitung während laufender Suche, Medien und Betrieb ohne JavaScript. Desktop 1440 und Mobil 390: kein horizontaler Überlauf, axe ohne Verstöße; Screenshots visuell geprüft. test:admin -- --admin-only bestätigt zusätzlich Menüs, Formulare, Bereiche, Speichern/Laden sowie die Vorschau mit aktivierter Bewegung; Tablet/Mobil ohne axe-Befunde. Berichte: reports/live-search/, reports/admin-quality/; Testkonten und Änderungen nur in isolierten Datenbankkopien.

Veröffentlicht: Commit 41d87f6 und Tag v1.7.1 auf origin; Release 1.7.1 ist das neueste stabile Release. ZIP, SHA-256-Datei und install.php hochgeladen und anschließend ohne Anmeldung heruntergeladen/bytegenau geprüft. Paket aus dem exakten Commit: 3531 Dateien, davon 208 versionierte Quelldateien verglichen; Produktionsabhängigkeiten ohne Entwicklungspakete, .env aus Vorlage, keine .env.local. Archivintegrität, Installer-Prüfsumme, Produktionscontainer/Cache und frische Installationsseite mit HTTP 200 geprüft. ZIP-SHA-256: 08846e3b8b700e66075160956a9e1963dc5d4f11a350dae175a0ff9511dd2ab1.

Grenzen und nächster Schritt: Das Update ist veröffentlicht und kann über die Systemverwaltung eingespielt werden; Kundenhostings wurden nicht verändert. MySQL wurde in diesem Schritt nicht ausgeführt; die lokale Prüfung verwendet SQLite. Öffentliche Lighthouse-Messungen wurden für diese Verwaltungsänderung nicht wiederholt; Nachweise aus 1.7.0 stehen unten.

1.7.0 – Installation, Verwaltungsabläufe und Bewegung (14.09.2026)

Veröffentlicht: Commit 495b268 und annotierter Tag v1.7.0 sind auf origin hochgeladen. Release 1.7.0 enthält ZIP, SHA-256-Datei und install.php. Gebaut aus dem exakten Commit: 3527 Archivdateien, 204 versionierte Quelldateien bytegenau verglichen, Produktionsabhängigkeiten ohne Entwicklungspakete. Archivintegrität, Prüfsumme im Installer, Produktionscontainer/Cache und frische Installationsseite (HTTP 200, .env vorhanden, keine .env.local) geprüft. Alle drei Anhänge anschließend ohne Anmeldung heruntergeladen und mit den lokalen Dateien verglichen; API meldet 1.7.0 als neuestes stabiles Release. ZIP-SHA-256: b708f7dffad74e1def31325533b646557963b0f3e702c46bd85ee109fc22a192. Das Update ist bereitgestellt, nicht auf Kundenhostings eingespielt.

Umgesetzt: .env ist die einzige automatisch geladene Installationsdatei und aus Git entfernt. Setup, Webinstaller und Admin-Pfad-Befehl schreiben dort. Alte .env.local wird einmalig mit Sicherung beider Originale zusammengeführt; wirksame Datenbank und Secret bleiben erhalten. System zeigt die tatsächliche Datenbank samt Quelle. Der Git-Updater erhält Konfiguration vor dem neuen PHP-Prozess und beim Rückweg; ein entdeckter falscher Datenbank-Sicherungspfad wurde ebenfalls korrigiert. Ein fehlgeschlagenes Zurückschreiben lässt die Website im Wartungszustand. Vorgehen beim erstmaligen Wechsel von altem Git-Updater-Code und bei FTP: UPDATE.md. Keine Schema-/Datenbankmigration.

Verwaltung: Fehler 500 beim Öffnen von Bereichen behoben. Seiten/Beiträge mit eigener Navigation, datenbankseitiger Pagination (20/50), Suche, Status, begrenztem Papierkorb und Filtererhalt. Elternauswahl unabhängig von der Ergebnisseite. Menüeditor mit Suchauswahl per Tastatur, eigenen Links, klarer Reihenfolge, Haupt-/Unterpunkten und Rückgängig; Validierungs-/Verbindungs-/Versionsfehler erhalten Eingaben. Formularmeldung mehrzeilig über die verfügbare Breite; Felddefinitionen auf schmalen Bildschirmen als beschriftete Gruppen. Mobil zugänglicher Kontolink.

Bewegung: Richtungen und Effekte werden beim Erscheinen tatsächlich unterschieden. Slider haben Pfeile, Zähler, optionale Punkte und Pause/Wiedergabe. Ein bereits geplanter Wechsel wird bei manueller Bedienung abgebrochen. Automatik wartet bei Hover, außerhalb des Bildschirms und in unsichtbaren Tabs; reduzierte Bewegung stoppt sie. Bewegung im Editor ausdrücklich testbar, iframe-Sandbox weiterhin ohne Skriptfreigabe. Kein JavaScript für die Lesbarkeit der Inhalte erforderlich.

Geprüft: PHP 8.4.22 Vollsuite 461 Tests/3577 Assertions bestanden; anschließend 10/10 Tests des finalen Updaters mit 206 Assertions, einschließlich echter Datenbankänderung vor dem Fehler und ausgefallenem Restore. npm.cmd test 30/30. Alle 43 Twig-Dateien und Produktionscontainer gültig. Vollständiger npm.cmd run test:editor bestanden, einschließlich neuer Links-/Rechtseinblendung, reduzierter Bewegung, Drag-and-drop, Undo/Redo, Speicher-/Veröffentlichungs-/Konfliktabläufen und sämtlichen axe-Prüfungen. node scripts/admin-quality-check.mjs --audit bestanden: isolierte Kant-IT-Kopie mit 4 Seiten/29 Beiträgen; Pagination ohne Überschneidungen, leere Suche, Seitengrößen und Grenzwerte; Menüsuche per Tastatur, Hierarchie/Undo/Speicherung sowie 422/409 ohne Eingabeverlust; breite Formularmeldung nach Neuladen; echte gemeinsame Sektion mit Text; Kopf/Fuß ändern und neu laden; mehrfaches Slider-Autoplay, manuelle Pause, Fortsetzen, reduzierte Bewegung und bedienbare Sandbox-Vorschau. Desktop/1024/390 px ohne Seitenüberlauf oder axe-Verstoß. Zusätzliche Prüfung ohne JavaScript und visuelle Screenshots bestanden. Kunden-/Pilotdatenbank bleibt eine nur gelesene Quelle; Testkonten und Änderungen liegen ausschließlich in temporären Kopien, deren Hintergrundaufgaben abgeschaltet sind.

Öffentliche Messung: Kant-IT-Startseite mit Originalmedien, Produktionsmodus, Lighthouse 13.4.1, je drei Läufe: Leistung mobil 99/99/99, Desktop 99/99/97, Median jeweils 99. Barrierefreiheit, Best Practices, SEO und Agentic Browsing in allen sechs Läufen 100. Lokaler PHP-Server mit Kompression/Cachekopfzeilen über bin/dev-router.php; keine Garantie für Hosting oder beliebige Kundeninhalte. Berichte: reports/admin-quality/lighthouse/summary.json, einzelne Läufe daneben; Editor: reports/editor/result.json; Verwaltungsabläufe: reports/admin-quality/result.json. Screenshots der Verwaltung, der mobilen Formulardefinition und des Piloten visuell geprüft.

Grenzen / nächster Schritt: Kein neuer Firefox-/Safari-/Screenreader- oder echter Touchgerätetest, keine CWV-Felddaten. Der Pilot enthält vereinzelt bereits gespeicherte HTML-Entities als Klartext. Die übrigen offenen Entwicklungsplan-Pakete bleiben bestehen. Installations- und Bedienkorrekturen nach Veröffentlichung über den vorhandenen Archiv-Updater auf der gewünschten Installation übernehmen. Arbeitsplan: ARBEITSPLAN-ADMIN-QUALITAET.md; Entscheidungen: ADR 076–078.

1.6.2 – Sichtbare leere Sektionen und Spalten (14.09.2026)

Veröffentlicht: Commit 6a044e5 und annotierter Tag v1.6.2 sind auf origin hochgeladen. Release 1.6.2 enthält ZIP, SHA-256-Datei und install.php. Aus dem exakten Commit mit Produktionsabhängigkeiten gebaut: 3521 Archivdateien, 199 Quelldateien gegen den Commit verglichen, Archivintegrität und Produktionscontainer geprüft. Alle drei Anhänge anschließend ohne Anmeldung heruntergeladen und gegen die lokalen Dateien geprüft; 1.6.2 ist laut API das neueste stabile Release. ZIP-SHA-256: 6af093d752fafa76c720c203e1e3ced03e37ace3481a405b7d6eed378255e449. Das Update ist bereitgestellt, nicht auf Kundenwebsites installiert.

Erledigt: Leere Sektionen, Zeilen und Spalten besitzen in der Vorschau sichtbare, beschriftete Arbeitsflächen. „Inhalt hinzufügen“ setzt einen genauen Einfügepunkt, öffnet den Werkzeugkasten und fokussiert die Suche. Leere Sektionen bieten unmittelbar 1–4 Spalten; neue Elemente werden sichtbar gescrollt. Beim Ziehen sind Container und Ablageziel erkennbar, die Vorschau scrollt am Rand mit. Spalten werden auf Desktop horizontal, bei mobiler Stapelung vertikal eingeordnet. Geöffnete Feinschliff-Gruppen bleiben auch bei schnellen Änderungen erhalten. Cachekennung 1.6.2; keine Schema- oder Datenbankmigration. Die Hilfen werden ausschließlich im Editor-DOM erzeugt und erscheinen weder in gespeicherten Dokumenten noch auf öffentlichen Seiten. Entscheidungen: ADR 075.

Referenz: Die tatsächlichen Builder-Dateien des bereitgestellten Flatsome 3.20.9 wurden erneut gelesen. Übernommen sind sichtbare Leerflächen, direkte Plus-Aktionen und schnelle Spaltenlayouts. Konkrete Fundstellen und Umfang stehen in docs/FLATSOME-ABGLEICH.md; keine laufende WordPress-Referenzinstallation geprüft.

Geprüft: npm.cmd test 30/30; PHP 8.4.22 vendor/bin/phpunit --filter EditorSecurityTest 27/27 mit 312 Assertions; JavaScript-Syntax und alle 19 Admin-Twig-Dateien gültig. Der vollständige isolierte npm.cmd run test:editor besteht einschließlich axe-Prüfungen. Neue Abdeckung: sichtbare leere Sektion, Tastaturwahl des Zweispaltenlayouts, Einfügen in genau die zweite Spalte, Ziehen in die erste Spalte mit Zielmarkierung und automatischem Scrollen, Undo/Redo, leere Layouts nach Speichern/Neuladen, Spaltenumsortierung Desktop/Mobil und Veröffentlichung ohne Editorhilfen. Desktop-, Mobil- und Drag-Ansichten anhand der Screenshots visuell geprüft. Bericht: reports/editor/result.json; Bilder: empty-section.png, empty-layout-desktop.png, empty-layout-mobile.png, empty-layout-drag.png im selben Ordner. Keine Änderungen an Kundendaten durch die isolierten Tests.

Bekannte Grenzen: Der öffentliche Renderer ist unverändert; Lighthouse wurde für diese Fassung nicht erneut gemessen. Die Messung zu 1.6.1 bleibt unten dokumentiert. Kein neuer Screenreader-, Firefox-, Safari- oder echter Touchgerätetest. Nächster Schritt: Die neuen Aufbauabläufe mit Umuts tatsächlichen Seiten nutzen und anschließend die restlichen Arbeitspakete des Entwicklungsplans fortsetzen.

1.6.1 – Editorbreite, neue Seitenabschnitte und passende Eigenschaften (14.09.2026)

Veröffentlicht am 14.09.2026: Commit b783d9d und annotierter Tag v1.6.1 sind auf origin (https://git.kant-it.de/kantarci/KantSites.git) hochgeladen. Das stabile Release 1.6.1 enthält ZIP, SHA-256-Datei und install.php. Das Archiv wurde aus dem exakten Commit mit Produktionsabhängigkeiten gebaut (3520 Dateien); Archivintegrität, Übereinstimmung mit dem Commit und Produktionscontainer wurden geprüft. Alle drei Anhänge anschließend ohne Anmeldung heruntergeladen und mit den lokalen Dateien verglichen; die API liefert 1.6.1 als neuestes stabiles Release. Archiv-SHA-256: a6e7da2bb14ede43dd2d94960b3b4d67cc285d9c717b560f796a91cbcde4dd26. Dieser Schritt veröffentlicht das Update, installiert es aber nicht auf Kundenwebsites.

Erledigt: Die festen 690/900/760-px-Grenzen für Text/H1/H2 sind entfernt. Elemente nutzen ihren Container, eigene Maximalbreiten bleiben möglich. Dauerhaft sichtbare Aktion Neue Sektion auf Seitenebene; auch der normale Sektionsklick erzeugt ohne expliziten Einfügepunkt einen übergeordneten Seitenabschnitt. Linke und rechte Sidebar sind per Ziehen und Tastatur verstellbar und merken ihre Breite im Browser. Die Vorschau nutzt ihre Fläche ohne Außenränder; die Elementbibliothek läuft nicht mehr horizontal über. Auf kleinen Fenstern passen sich die Panels an, auf dem Telefon stehen sie untereinander.

Feinschliff: Bild-, Text- und Containeroptionen werden passend gefiltert und sortiert. Gespeicherte Werte bleiben sichtbar; die gesamte Eigenschaftenauswahl ist über eine Schaltfläche erreichbar. Die Auswahl eines anderen Blocks setzt die Panelrolleiste nach oben. Geöffnete Eigenschaftengruppen bleiben beim Ändern eines Werts geöffnet. Automatische Größen und „Ohne Begrenzung“ sind explizit wählbar. Cachekennung auf 1.6.1 angehoben; keine Datenbankmigration, keine Kundeninhalte durch Tests geändert. Begründung und Rückweg: ADR 074.

Geprüft: npm.cmd test: 30/30; PHP 8.4.22 vendor/bin/phpunit: 452/452, 3476 Assertions. JavaScript-Syntax und alle 19 Admin-Twig-Dateien gültig; Produktionscache erfolgreich aufgewärmt, git diff --check sauber. npm.cmd run test:editor -- --audit vollständig bestanden: Seitenebenen/Undo/Redo, Panelbreiten per Maus/Tastatur und nach Neuladen, 390/850/1100/1920-px-Fenster, kontextbezogene Eigenschaften und bewusst gesetzte Textbreiten; auch Speichern, Neuladen, Veröffentlichen und Konfliktbehandlung. Sämtliche enthaltenen axe-Prüfungen ohne Verstoß. Desktop-, Bild- und Telefonansicht anhand der Browser-Screenshots visuell geprüft. Lighthouse 13.4.1, isolierte öffentliche Editortestseite im Produktionsmodus: alle fünf Kategorien 100, jeweils drei Läufe mobil und Desktop, auch jeder Einzellauf 100. Kein Nachweis für beliebige Kundeninhalte oder Feldwerte. Berichte: reports/editor/result.json, reports/lighthouse-editor/summary.json; Ansichten: reports/editor/flexible-workspace.png, context-image.png, editor-phone.png.

Bekannte Grenzen: Die geänderten Standardbreiten können den Zeilenumbruch bestehender Seiten verändern. Sektions- und ausdrücklich gespeicherte Elementbreiten bleiben wirksam. Kein neuer Screenreader- oder Felddatennachweis. Nächster Schritt: Umuts tatsächliche Editorabläufe mit realen Seiten durchgehen, danach den verbliebenen Ausbau gemäß Entwicklungsplan fortsetzen.

Vorheriger Stand und bisherige Arbeitspakete

Stand: 14.09.2026, Fassung 1.6.0. Das System heißt KantSites (vorher Arbeitstitel „Kant CMS“). Startseite von 18 auf 8 Datenbankabfragen, gemessenes Ladebudget, alle langen Listen blättern. Sicherung und Wiederherstellung, letztere nachgewiesen. Globale Seitenbreite wirkt jetzt auch nach oben, vier Menüplätze mit drei Menüarten, und jede Seite wählt ihren Kopf- und Fußbereich – auch „keiner“. 93 Gestaltungseigenschaften, neun Einblendungen, sieben Hover-Effekte, Symbol, Fortschritt und ein Slider ohne JavaScript. Update über Git mit Vorprüfung, Sicherung und Rückweg. Statistik ohne Wiedererkennung und damit ohne Einwilligung. Einwilligung mit tatsächlich zurückgehaltenen Einbettungen, Datenschutz und Impressum als Pflichtseiten. Benutzerverwaltung in der Oberfläche, Mediathek als eigene Seite mit Blättern. Die Verwaltung hat einen gemeinsamen Rahmen mit einer Navigation, die zeigt, wo man ist. Eigenes CSS je Element für alle, eigenes Skript nur für Administratoren und nur als eigene Datei. Formatierter Text mit Werkzeugleiste und HTML-Ansicht, serverseitig gegen eine Positivliste gefiltert. Kopf- und Fußbereich sind frei gestaltbare Blockdokumente, dazu verknüpfte wiederverwendbare Sektionen (Arbeitspaket G, Teil 1). Frei wählbare Startseite; /sitemap.xml und Geschwister waren lokal nie erreichbar und sind es jetzt. Seiten verschieben mit automatischen Weiterleitungen, Papierkorb, Fassungsverlauf und geplante Veröffentlichung (Arbeitspaket F). Browsertests wiederhergestellt und erweitert; dabei drei echte Fehler gefunden und behoben, darunter die in der Vorschau wirkungslose Gestaltung. Direktbearbeitung in der Vorschau, Kontrastwarnung, Gestaltungsvorlagen und Bildauswahl für Hintergrundbilder. Der Editor gestaltet frei statt über feste Auswahllisten: eigene Farbpalette, Schriften, und jede Eigenschaft je Block, je Gerät und für den Hover-Zustand frei einstellbar (Dokumentschema v10). SEO-Grundlage mit Sitemap, robots.txt, Weiterleitungen und strukturierten Daten (Arbeitspaket E). Formulare mit serverseitiger Validierung, Einwilligung, E-Mail-Versand und Spamschutz (Arbeitspaket D). Tabs als fortschreitende Verbesserung mit JavaScript (Dokumentschema v7). Zehn neue Blöcke für die Abschnitte der Pilotstartseite (Arbeitspaket C). Beiträge mit Datum, Autor, Auszug, Beitragsbild, Kategorien, Schlagwörtern, Archiven, einbettbarer Beitragsliste und RSS (Arbeitspaket B). Menüs mit Seiten-, Anker- und externen Zielen ersetzen die fest verdrahtete Navigation (Arbeitspaket A). Seiten und Beiträge lassen sich im Browser anlegen und löschen, mit Adresshierarchie bis vier Ebenen. Die vollständige Phase-1-Abnahme bleibt wegen des fehlenden UX-Builder-Referenzabgleichs offen.

Erster Commit angelegt (82b0782); das Projekt hat damit einen Rückfallpunkt. Noch kein Remote und kein Push.

Für den Livegang vorbereitet (13.09.2026): docs/LIVEGANG.md trennt, was geprüft ist, von dem, was nur auf dem echten Hosting geprüft werden kann, und nennt die Reihenfolge am Umzugstag. 14 Weiterleitungen von den alten WordPress-Adressen auf die neuen sind angelegt und beantwortet (/productsolutions → /projekte, die gekürzten Beitragsadressen, /category/allgemein); ohne sie hätte jede bei Google stehende Adresse nach dem Umzug mit 404 geantwortet. In der Datenschutzerklärung wurde der Abschnitt über Google Fonts an die Wirklichkeit angepasst (die Schriften kommen jetzt vom eigenen Server) und die Abschnitte über Google Maps und reCAPTCHA entfernt, weil beides nicht eingebunden ist – eine Erklärung, die Datenflüsse beschreibt, die es nicht gibt, ist so falsch wie eine, die welche verschweigt. Nicht geprüft und nicht prüfbar von hier: der E-Mail-Versand.

Verwaltung und Updateweg (1.4.3, 13.09.2026): Die Farbfelder im Designbereich haben einen Farbwähler samt Palettenauswahl; das Textfeld bleibt maßgeblich, damit @akzent ein Verweis bleibt und nicht beim ersten Klick zu einem festen Hexwert erstarrt (ADR 066). Die Verwaltung ist auf dem Telefon bedienbar: das Menü ist unter 760 px ein zugeklapptes <details>, das den aktuellen Bereich nennt, mit 44 px hohen Zielen – ohne Skript. Die zwölf Navigationssymbole sind eigene SVG-Pfade statt Unicode-Zeichen, die ein Telefon zu bunten Emoji macht (ADR 067). Und es gibt ein Update ohne Git: ArchiveUpdate lädt das Releasearchiv, vergleicht die Prüfsumme, sichert, ersetzt die Dateien und führt die Migrationen in einer zweiten Anfrage aus – der einzige Updateweg auf geteiltem Hosting (ADR 068, docs/UPDATE.md).

Nachbau nachgezogen (13.09.2026): Der Fußbereich ist jetzt vierspaltig wie im Original – Zeichen und Satz, „Leistungen", „Unternehmen", „Kontakt", darunter die Zeile mit Copyright. Die beiden Linklisten sind Menüs, keine Dokumentinhalte: sie lassen sich in der Verwaltung pflegen. Alle 29 Beiträge haben jetzt Text – die elf fehlenden sind neu geschrieben, denn auch im Original stehen dort nur Bildergalerien ohne Beschreibung. Die Karte „Bereit für den nächsten Schritt" hat 34 px Abstand zu den Kontaktzeilen darunter. Nicht erledigt: das TeamViewer-Logo im Fernwartungsbereich – dazu unten.

Vom Original übernommen (13.09.2026): die Meta-Beschreibung der Startseite im Wortlaut von kant-it.de, das Symbol der Website (dort 192 × 192) und die Bot-Regeln der robots.txt. Titel, Canonical, Open Graph, strukturierte Daten (LocalBusiness statt Yoasts ProfessionalService), Sitemap, robots.txt und llms.txt entstehen in KantSites aus dem Inhalt und stimmen mit dem Original überein. Beim Abgleich aufgefallen: das og:image des echten kant-it.de zeigt auf wp-content/uploads/kant-it-share.png – die Datei antwortet mit 404, beim Teilen erscheint dort also kein Bild.

Im Nachbau von kant-it.de gefunden und behoben: vier tote Verweise im Hero-Slider – die Adressen waren im Aufbauskript von Hand geschrieben, die Beiträge bekamen längere. Sie werden jetzt aus derselben Regel abgeleitet, mit der die Beiträge entstehen, und ein zweiter Lauf des Skripts aktualisiert vorhandene Seiten, statt sie zu überspringen. Eine Prüfung aller internen Verweise auf 404 gibt es im CMS noch nicht; gefunden wurden sie mit einem einmaligen Durchlauf im Browser.

Wiederkehrende Aufgaben ohne Cronjob (14.09.2026, ADR 073): Bis hierher stand in vier Dokumenten „gehört in einen Cronjob“ – auf geteiltem Hosting oft nicht einlösbar, und die Folge war nicht „läuft seltener“, sondern läuft nie. Jetzt gibt es einen Aufgabenplan mit vier Auslösern: jeder Seitenaufruf (nach der ausgelieferten Antwort, in kernel.terminate), das geöffnete Backend (im Minutentakt, nur solange der Reiter sichtbar ist), ein URL-Cronjob unter /cms-cron/<Schlüssel> für Hoster, die nur eine Adresse aufrufen können, und die Konsole (cms:tasks:run, auch als Dauerläufer mit --loop). Fünf Aufgaben sind angeschlossen: geplante Veröffentlichung (5 Minuten), Statistik verdichten, Sicherung, Updateprüfung und Aufräumen (je täglich). Der Bildschirm Aufgaben zeigt Plan, letztes Ergebnis, Dauer, Auslöser und Protokoll, schaltet jede Aufgabe einzeln und ändert ihren Abstand. Die Einschränkung steht auf diesem Bildschirm: ohne Besuch und ohne geöffnetes Backend passiert nichts – eine Aufgabe für 03:00 Uhr läuft auf einer stillen Website erst um 07:15 Uhr.

Erledigt

  • AGENTS.md und ENTWICKLUNGSPLAN.md unverändert aus Downloads im Projektstamm abgelegt; SHA-256 jeweils mit dem Original identisch.
  • Leeres lokales Git-Projekt auf main initialisiert; Quellen und Lockfiles für den ersten Commit vorgemerkt. Noch kein Commit und kein Remote/Push.
  • Nutzerentscheidung „herkömmliches PHP-Webhosting“ übernommen. Symfony 7.4 / Twig / Doctrine gewählt; kein Python-/Django-Anwendungscode.
  • Start-/Build-/Prüfkommandos und Hostingvoraussetzungen in README, begründete Architektur und Datenmodell in ARCHITECTURE, Plananalyse/offene Anforderungen in REQUIREMENTS.
  • Erstes Datenbankschema, versionierte Blockdokumente, unveränderliche Revisionen über den Repository-Vertrag, getrennte Entwürfe/Veröffentlichungen und optimistische Schreibkonflikterkennung.
  • Drei gespeicherte öffentliche HTML-Demoseiten, lokale CSS-Ausgabe ohne JavaScript, Metadaten/Canonical, 404, Sprunglink und sichtbarer Fokus. /home leitet auf / um.
  • Idempotentes Demo-Seeding und lokaler CLI-Import validierter Entwürfe. Keine ungeschützte HTTP-Schreibschnittstelle.
  • Symfony-Security-Anmeldung, CSRF, Login-Throttling, geschützte Sessions und Rollen Editor/Publisher/Admin. Neue Migration ergänzt ausschließlich die Benutzertabelle. Passwort-Rehash hält Sessionbenutzer und Datenbank synchron; Rollenänderungen invalidieren bestehende Sitzungen.
  • Seitenübersicht und visueller Editor: Elementbibliothek, Strukturbaum, gemeinsame serverseitige Live-Vorschau, Eigenschaften, Drag-and-drop und Schaltflächen zum Verschieben/Verschachteln, Duplizieren/Löschen, lokales Undo/Redo.
  • Inhalt-/Designbereiche mit typabhängigen Abständen, Ausrichtung, Schriftgröße/-stärke, Zeilenhöhe, Spaltenaufteilung/-abstand und Ecken. Gerätewerte Desktop → Tablet → Mobil samt Vererbung, Einzel-/Gerätereset und Undo. Echte Vorschau-Layoutbreiten 1240/768/390 px mit visueller Skalierung.
  • Drei bearbeitbare Abschnittsvorlagen (Intro, zwei Leistungen, Kontaktaufruf), einklappbarer Werkzeugkasten, Struktursuche mit Vorfahren, einklappbare Teilbäume, Pfadnavigation und dauerhaft erreichbare Blockaktionen.
  • Dokumentformat v2, erhaltender idempotenter v1-Konverter, gemeinsame Tokenliste für Validator/Editor/CSS und dokumentierter Migrations-/Rückweg. Bestehende v1-Seiten werden beim Öffnen nicht geändert; keine neue DB-Migration erforderlich.
  • Dokumentformat v3 für Galerie, Bildbeschriftungen/-varianten und Effekte, mit erhaltendem Upgrade ohne Downgrade und Undo. v1/v2 bleiben unterstützt. Schatten, Hover-Anheben/Bildzoom und Einblenden beim Laden ohne öffentliches JavaScript; Bewegungsreduktion wird berücksichtigt.
  • Medienverwaltung im Editor und unter /admin/media: JPEG/PNG/WebP-Upload, echte Bildvalidierung, Größen-/Pixel-/Speichergrenzen, WebP-Varianten bis 480/960/1600/2400 px (ohne Hochskalieren, Hintergrundbilder geräteabhängig), Suche, Titel/Alt-Text, Archivieren/Wiederherstellen und Schreibkonflikterkennung. Bildauswahl für einzelne Bilder und Galerien, kontextbezogene Alt-Texte/dekorative Verwendung und Beschriftungen.
  • Neue Medientabelle durch Migration 20260912000300 lokal angewendet. Vorher konsistentes SQLite-Backup unter var/backups/before-media-20260912-051258.db. Bestehende Seiten/Benutzer nicht geändert. Lokal läuft PHP 8.4 mit 8M Upload-, 10M POST- und 256M Speicherlimit; originale Uploads werden nicht gespeichert.
  • Speichern/Laden von Entwürfen, separates Veröffentlichen mit Bestätigung, sichtbare Speicher-/Fehler-/Konfliktzustände, JSON-Sicherung und bewusster Server-Neuladedialog. Keine Tests schreiben in den echten Seitenbestand.
  • Auf ausdrücklichen Nutzerwunsch lokalen Administrator umut mit zufälligem Passwort angelegt und im In-App-Browser erfolgreich angemeldet. Das Passwort steht in keiner Projektdatei; nur der Hash in der lokalen Datenbank. Keine Browser-Passwortspeicherung ausgelöst.
  • Seitenerstellung und -löschung im Browser: POST /admin/pages (ab ROLE_EDITOR) legt Seite oder Beitrag mit gültigem Startdokument als privaten Entwurf an; POST /admin/pages/{id}/delete (nur ROLE_ADMIN) entfernt eine Seite mit allen Revisionen. Beide mit CSRF-Token, Fehlermeldungen als Flash und ohne JavaScript bedienbar.
  • Adresshierarchie: neue Spalten path, parent_id, type, created_at über Migration 20260912000400; websiteweit eindeutiger Pfad statt eindeutigem Slug, Tiefe bis vier Ebenen, Beiträge ohne Unterseiten, reservierte Adressen und Adressableitung aus dem Titel. Öffentliche Route bedient verschachtelte Pfade wie /leistungen/webdesign; ein Lookahead hält /admin, /media und /assets aus dem Auffangmuster.
  • Ladeverhalten gemessen statt vermutet (ADR 039, Arbeitspaket M): ein Zähler liefert die Datenbankabfragen je Anfrage im Kopf X-Kant-Queries (außerhalb der Produktion). Die erste Messung zeigte 18 Abfragen auf der Startseite, weil Einstellungen und Theme je Aufrufer erneut gelesen wurden; beide merken sich ihren Stand jetzt für die Dauer einer Anfrage – 18 → 8. Die Browserprüfung misst je Seite HTML, CSS, Anfragen, Skripte und Abfragen gegen Obergrenzen: Startseite 7,4 KB HTML, 22,7 KB CSS, 2 Anfragen, 0 Skripte.
  • Aufgabenplan ohne Cronjob (ADR 073, Migration 20260914000100): eine Zeile je Aufgabe mit Fälligkeit, Sperre und letztem Ergebnis. Wer sie ausführen darf, entscheidet ein bedingtes UPDATE – auf MySQL wie auf SQLite atomar, damit zwei gleichzeitige Besucher eine Aufgabe nicht zweimal starten; die Sperre verfällt nach 15 Minuten von selbst. Der Normalfall kostet keine Datenbankabfrage: in var/scheduler.json steht, wann überhaupt das nächste Mal etwas ansteht. Ein Fehlschlag ist ein sichtbarer Zustand mit wachsendem Abstand bis zum nächsten Versuch (Verdopplung, gedeckelt bei sechs Stunden), und eine scheiternde Aufgabe reißt nie eine Seite mit. Neue Aufgaben sind eine Datei: wer App\Ops\Tasks\Task implementiert, steht beim nächsten Abgleich im Plan. Befehle: cms:tasks, cms:tasks:run (--task, --limit, --budget, --loop, --max-time).
  • Das erste Bild einer Seite lädt sofort, alles darunter weiterhin verzögert. Es ist meistens das größte Ding über der Falz.
  • Slider mit Punkten ohne Skript (ADR 044): ::scroll-marker zeichnet je Folie einen Punkt, der aktive ist erkennbar, die Rollleiste entfällt; ältere Browser behalten das wischbare Schiebefeld. Beitragskarten sind echte Karten (Bild 16:10, 18 px Ecken). Überschriften dürfen ein Wort hervorheben (<mark>, in Akzentfarbe). Behoben: die Theme-Schriftskala überschrieb die Größen in Karten und Aufruf-Bändern.
  • „Weiterlesen" ist jetzt ein Link. In der wechselseitigen Beitragsliste stand dort eine reine Beschriftung: sie sah aus wie ein Knopf und war keiner. Der Titel steckt für Vorlesesoftware im Linktext, damit nicht zwölf Links gleich heißen.
  • Behoben in 1.4.5: die Verwaltung zeigte Git-Fehler auf einer Installation ohne Git (ADR 072). git installiert und „Arbeitskopie" waren derselbe Schalter; auf Umuts Hosting ist Git vorhanden, die Installation kam aber aus dem Releasearchiv. Dort stand fatal: Not a git repository an der Stelle der Fassungsnummer und eine rote Vorprüfung für Bedingungen, die gar nicht gelten. Ohne Arbeitskopie verschwinden die Git-Karten jetzt.
  • Behoben in 1.4.5: „Sicherung anlegen" endete mit 500 (ADR 072). VACUUM INTO gibt es erst seit SQLite 3.27; Umuts Hosting hat eine ältere Fassung. Bei älteren wird unter einer Schreibsperre kopiert, und der Sicherungsknopf fängt jeden Fehler ab, statt mit einer 500-Seite zu enden.
  • System nennt die tatsächlich benutzte Datenbank und deren Quelle (1.7.0, ADR 076): Art, Dateipfad, Größe, Schreibrecht und .env beziehungsweise Server-Umgebung. Die bereits lokal begonnene Diagnose wurde in den neuen Konfigurationsvertrag übernommen. Verdeckte Datei-Overlays entfallen; die bisher wirksamen Werte werden einmalig gesichert und in .env zusammengeführt.
  • Das Update schreibt mit (1.4.6, ADR 071): Jeder Schritt des Archiv-Updaters geht sofort nach var/updates.log – während des Laufs, nicht am Ende. Auf Umuts Hosting war die Ergebnisseite tot, und damit war auch verloren, was der Updater berichtet hatte; genau so wurde der nächste Fehlschlag dann doch noch lesbar („bin ließ sich nicht beiseitelegen – der alte Stand wurde zurückgeschoben"). Die Ergebnisseite versteht zusätzlich die Schreibweise bis 1.4.4: Beim Update schreibt der alte Code die Schritte und der neue zeigt sie an.
  • Behoben in 1.4.5: der Updateknopf endete mit 500 (ADR 071). Auf Umuts Hosting gefunden, mit einer echten 1.4.3-Installation nachgestellt: Das Update lief vollständig durch – alle neun Schritte, Dateien getauscht, var/ und .env unberührt –, aber die Ergebnisseite starb daran. Der Updater über Git nennt einen Schritt step, der über das Archiv nannte ihn label, beide legen ihn unter demselben Sitzungsschlüssel ab, und die Vorlage liest step. Damit war auch der zweite Knopf unerreichbar und die Website blieb im Wartungszustand. Zehn Tests prüften das Rückgabe-Array, keiner die Seite, die es anzeigt; der neue Test rendert sie und wurde gegengeprüft, indem der Fehler noch einmal eingebaut wurde.
  • Migrationen im Browser ausführen (1.4.4, ADR 070): Wer ein Update von Hand über FTP einspielt, hat danach neuen Code und eine alte Datenbank. Der Bildschirm „System" zeigt jetzt eine Karte, sobald etwas aussteht, nennt die Namen und führt sie auf Knopfdruck aus – vorher ging das nur über eine Konsole, also genau das, was auf geteiltem Hosting fehlt. Der Weg von Hand steht Schritt für Schritt in docs/UPDATE.md.
  • Ein Fehlschlag beim Laden sagt, woran es lag (1.4.4, ADR 070): „Die Releaseseite antwortet nicht" war drei Ursachen in einem Satz. Beim Gegenprüfen des Releases fiel auf, dass diesem PHP der Zertifikatsspeicher fehlt – auf einem Webhosting der häufigste Grund und der einzige, den man in einer Minute behebt. Die Meldung nennt ihn jetzt, und bei genau diesem Fehler wird der zweite Weg über den Dateizeiger versucht. Abgeschaltet wird die Prüfung nicht.
  • Das Hintergrundbild des ersten Abschnitts wird vorab geholt (1.4.3, ADR 069): Auf der Startseite des Piloten ist das größte Element über der Falz kein <img>, sondern der Hintergrund des Heros – und der steht nur im Stylesheet, das der Browser erst nach dem HTML holt. Drei Vorabverweise mit denselben Gerätegrenzen wie der Compiler holen genau die Variante, die die Regel auch nimmt; ein imagesrcset hätte bei manchen Fensterbreiten ein zweites Bild geladen. Gemessen: LCP 1,3 s → 1,1 s, Leistung Desktop 96 → 97, Mobil 98 → 99 (auf dem Entwicklungsserver ohne Kompression). Dabei gefunden: dasselbe Bild wurde je Geräteregel neu abgefragt – die Mediathek merkt sich jetzt, wonach sie in dieser Anfrage schon gefragt wurde, Startseite 19 → 15 Abfragen.
  • Update aus dem Releasearchiv (1.4.3, ADR 068): der Weg für Installationen ohne Git – bisher stand dort nur „Dieses Verzeichnis ist keine Git-Arbeitskopie". Zwei Knöpfe im Bildschirm „System": der erste lädt, prüft die Prüfsumme, sichert und ersetzt die Dateien, der zweite führt die Migrationen aus – in einer eigenen Anfrage, weil ein Prozess, der sich selbst ausgetauscht hat, mit dem alten Code weiterarbeitet. var/, .env, hochgeladene Schriften und das erzeugte Stylesheet-Bündel bleiben unberührt, public/ wird dazu Datei für Datei zusammengeführt. Der alte Stand liegt danach unter var/update/. Geprüft mit acht Tests gegen eine Releaseseite im Speicher (tests/ArchiveUpdateTest.php), einer davon hält die Ersetzungsliste gegen die Mitnahmeliste des Releaseskripts.
  • Eigene Symbole in der Verwaltung (1.4.3, ADR 067): zwölf SVG-Pfade statt Unicode-Zeichen, die auf dem Telefon als Emoji in fremdem Stil erscheinen. Ein Test findet Emoji-Zeichen in der Navigationsvorlage und zwei Einträge mit demselben Pfad – beides war vorhanden.
  • Verwaltung auf dem Telefon (1.4.3, ADR 067): das Menü klappt unter 760 px zu und nennt in der Zusammenfassung den Bereich, in dem man steht; Ziele 44 px hoch, kein Skript, kein seitliches Scrollen. Geprüft bei 390 px.
  • Farbwähler im Designbereich (1.4.3, ADR 066): Farbfeld und Palettenauswahl neben dem Textfeld, das maßgeblich bleibt – ein <input type="color"> allein hätte @akzent in einen festen Wert verwandelt und die Verbindung zur Palette gekappt. Ohne JavaScript bleibt das Textfeld.
  • Behoben in 1.4.2: die Systemprüfung nennt jetzt die SQLite-Fassung. Auf dem Hosting scheiterte die Installation im dritten Schritt mit „near "(": syntax error" – dort läuft SQLite in einer Fassung vor 3.16, und Doctrine liest das Schema mit Tabellenfunktionen (pragma_table_info(...)), die es erst seit 3.16 gibt. Die Prüfung nennt die gefundene Fassung vor der Wahl der Datenbank, und der Datenbankschritt bietet SQLite gar nicht erst an, wenn sie zu alt ist – mit dem Hinweis auf MySQL. Eine Voraussetzung, die erst mitten in der Installation auffällt, ist keine Prüfung wert.
  • Behoben in 1.4.1: dem Releasearchiv fehlten migrations.php und cli-config.php. Auf dem Hosting brach die Installation deshalb im dritten Schritt ab – „The configuration file does not exist", genau dort, wo die Tabellen entstehen sollten. Die beiden Dateien standen weder in der Mitnahme- noch in der Ausschlussliste des Releaseskripts, also fiel niemandem auf, dass sie fehlen. Ein Test ordnet jetzt jeden Eintrag im Projektstamm einer der beiden Listen zu; was neu dazukommt und unbedacht bleibt, lässt ihn fehlschlagen. Der Installationsweg wurde anschließend vollständig durchgespielt: 3504 Dateien entpackt, 14 Migrationen ausgeführt, 15 Tabellen angelegt.
  • Fassung 1.4.0 ist veröffentlicht und der Installationsweg einmal vollständig durchgespielt: install.php aus dem Release geladen (anonym, ohne Anmeldung), in ein leeres Verzeichnis gelegt, aufgerufen – es lädt kantsites-1.4.0.zip (4,7 MB, 3502 Dateien mit vendor/), prüft die SHA-256-Summe, entpackt und löscht sich selbst. Die frische Installation antwortet mit der Systemprüfung unter /install. Das Repository ist dafür öffentlich – ein privates liefert die Downloadadresse ohne Anmeldung nicht aus.
  • Die Verwaltung kann umziehen (ADR 065): php bin/console cms:admin:path meine-verwaltung legt sie auf eine andere Adresse – geprüft wird, dass der Name frei ist und keine Seite verdeckt. Der Wert steht in .env.local und gilt für Routen, Firewall, Schutzkopfzeilen und Statistik. In der robots.txt wird ein geänderter Name nicht genannt; beim Standardnamen admin bleibt die Zeile, weil diese Adresse ohnehin bekannt ist. Die Systemseite zeigt die aktuelle Adresse an.
  • Dokumentschema v19: Baustein Rechtszeile (Impressum, Datenschutz, „Einwilligung ändern") für den Fußbereich – gestaltbar und an der richtigen Stelle statt als heller Streifen unter einem dunklen Fuß; die eingebaute Leiste erscheint nur noch, wenn die Seite den Baustein nicht hat. Dazu „Beim Klick" je Bild (nichts / vergrößern / Adresse) und „Bilder vergrößerbar" je Galerie.
  • Bildbetrachter (ADR 063): ohne JavaScript ein Verweis auf die große Bildfassung, mit JavaScript ein Betrachter über der Seite – <dialog> mit Blättern, Escape, Pfeiltasten und Zähler. Das Skript lädt nur auf Seiten, die ein vergrößerbares Bild enthalten.
  • Knopf „nach oben" (ADR 062), abschaltbar und vollständig einstellbar: Seite, Form, Größe, Farben, Schwelle, Beschriftung. Ein Anker ohne Skript; eingeblendet über eine Scroll-Zeitachse, wo der Browser sie kennt.
  • Behoben: zwei Stylesheet-Regeln griffen seit drei Veröffentlichungen nicht mehr (ADR 064). Beim Entfernen alter Regeln waren Selektorreste stehen geblieben; CSS nimmt dann die nächste Regel mit. Betroffen waren der Einwilligungsdialog und die Rechtszeile – beide standen ungestaltet unter dem Fuß. Ein Test prüft jetzt jede CSS-Datei auf abgebrochene Selektoren.
  • Menüs kosten nicht mehr je eine Abfrage. Jedes Menü holte die Seitenliste selbst; mit vier Menüs im Fuß waren das vier gleiche Abfragen je Seitenaufruf. Jetzt einmal je Anfrage gemerkt – Startseite des Nachbaus 21 → 18 Abfragen.
  • Behoben: die Vorschau zeigte beim Aufbau bis zu 1440 ms lang eine Seite ohne Gestaltung (ADR 061). Die Stile wurden erst angewandt, wenn das letzte Bild geladen war. Jetzt sitzen sie, sobald das Dokument geparst ist, und der Rahmen bleibt bis dahin verdeckt. Die Farben selbst stimmten immer – Vorschau und veröffentlichte Seite geben nachweislich dieselben berechneten Werte.
  • Cloudflare Turnstile für Formulare, optional (ADR 059): aus, bis in den Einstellungen beide Schlüssel stehen; dann erscheint die Prüfung in jedem Formular, und die Sicherheitsrichtlinie öffnet sich nur auf Seiten mit Formular für challenges.cloudflare.com. Antwortet Cloudflare nicht, wird die Anfrage trotzdem angenommen. Die eingebauten drei Prüfungen ohne Dritte bleiben unabhängig davon bestehen. Der Preis steht in der Verwaltung dabei: das Widget braucht JavaScript.
  • Behoben: die Fehlerseite eines Formulars hatte keine Gestaltung (ADR 060). Wer ein Pflichtfeld vergaß, bekam eine nackte Seite – der Formularcode baute sie selbst auf und ließ Theme, Blockstile und Schriften weg. Jetzt rendert derselbe Controller wie sonst auch.
  • Die Schriften liegen im Repository (public/assets/fonts/, mit lato-OFL.txt und LIZENZ.md daneben, wie die Open Font License es verlangt): auf einem frischen Server fehlt damit nichts, und es wird nie versehentlich von Google geladen. Andere Installationen holen ihre eigenen mit cms:fonts:fetch.
  • docs/NEUE-WEBSITE.md: die Anleitung, mit der eine neue Kundenwebsite entsteht – Reihenfolge, Dokumentformat, Bilder, SEO, Prüfläufe, und am Ende die fünf Fehler, die in diesem Projekt immer wieder passiert sind.
  • Schutzkopfzeilen ergänzt und geprüft: Permissions-Policy (Kamera, Mikrofon, Standort und Sensoren abgeschaltet) auf öffentlichen Seiten und in der Verwaltung; X-Powered-By wird entfernt – der Server nannte bisher in jeder Antwort seine genaue PHP-Fassung. Die Browserprüfung misst die Kopfzeilen jetzt, statt sie anzunehmen.
  • Behoben: der Editor kannte Schema v17, der Validator v18. In einem aktuellen Dokument war die Beitragsnavigation nicht einfügbar. Ein Test liest die höchste Fassung jetzt aus dem Validator und vergleicht sie mit der des Editors – dieselbe Falle war schon bei v13 zugeschnappt.
  • Getrennter Titel für Suchergebnisse (Migration 20260913000100): jede Seite und jeder Beitrag kann einen eigenen <title> tragen, unabhängig von der Überschrift auf der Seite. Leer heißt weiterhin: der Titel der Seite gilt. Er zählt auch für og:title.
  • Schriften kommen vom eigenen Server (ADR 057): ein Knopf im Designbereich oder php bin/console cms:fonts:fetch lädt die gewählten Google-Schnitte als WOFF2 nach public/assets/fonts und schreibt die @font-face-Regeln daneben. Damit lädt keine Seite mehr etwas von Google – das spart zwei fremde Verbindungen nacheinander (gemessen 820 ms) und die datenschutzrechtliche Angreifbarkeit gleich mit. Ohne Dateien bleibt es beim bisherigen Verweis.
  • Ein Stylesheet statt vier, und eine .htaccess, die etwas tut (ADR 056): gebündelt, ohne Kommentare, mit Prüfsumme im Dateinamen; Kompression, ein Jahr Cache für Dateien mit Fassung oder Kennung in der Adresse, Schutz vor dem Ausliefern interner Dateien, HTTPS-Umleitung und HSTS vorbereitet und auskommentiert. Der Entwicklungsserver sendet dieselben Kopfzeilen, damit lokale Messungen etwas bedeuten.
  • Bilder in der Größe, in der sie erscheinen (ADR 058): ein Hintergrundbild, das nur für „Desktop“ gesetzt wurde, gilt auf dem Telefon in der Telefongröße (gemessen 189 KB → 40 KB); <img> nennt seine feste Breite, wenn es eine hat, und sonst sizes="auto"; fetchpriority="high" liegt auf dem ersten Bild des Inhalts statt auf dem Logo im Kopfbereich.
  • Lighthouse gegen den Nachbau von kant-it.de (docs/quality/lighthouse-kantit.json, vier Adressen, je drei Läufe, Median): Barrierefreiheit, Best Practices, SEO und „agentic browsing“ 100 auf allen geprüften Seiten, mobil wie am Schreibtisch. Performance: Projekte-, Beitrags- und Impressumsseite 100/100, Startseite 98 mobil / 96 am Schreibtisch – dort liegen vier Sliderbilder und ein großflächiges Hintergrundbild über der Falz. Gemessen im Produktionsmodus mit denselben Kopfzeilen, die public/.htaccess setzt; keine Felddaten und keine Messung auf echtem Hosting.
  • Verweise werden geprüft (npm run test:links): folgt der Sitemap, sammelt jeden internen Verweis samt Bildern, Stylesheets und Hintergrundbildern und meldet jede Antwort ab 400. Im Nachbau 408 Adressen, kein toter Verweis.
  • Das Vorschaubild beim Teilen erscheint jetzt tatsächlich (ADR 056): og:image wird aus dem Feld der Seite aufgelöst, ersatzweise aus dem Beitragsbild – vorher speicherte das Feld und bewirkte nichts, keine KantSites-Seite hatte ein Teilen-Bild. Dazu og:site_name, og:locale, og:image:alt, twitter:card (klein ohne Bild, groß mit) und das Bild im strukturierten Datensatz eines Beitrags. Archive haben jetzt ebenfalls Open-Graph-Angaben.
  • Eigene robots.txt-Zeilen: 200 statt 30, Leerzeilen bleiben (ADR 056), damit sich Regeln je Bot in Blöcken schreiben lassen – etwa welche KI-Crawler lesen dürfen. Ein Sitemap-Index ist bewusst nicht gebaut: nötig wird er ab 50.000 Adressen.
  • Beiträge sind optional (ADR 053): ohne einen einzigen veröffentlichten Beitrag antworten /beitraege und /feed.xml mit 404, der Feedverweis fehlt im Kopf, die Sitemap kennt kein Archiv, und ein Beitragslisten-Baustein zeigt öffentlich nichts – der Hinweis „hier erscheinen Beiträge“ bleibt nur im Editor. Ein Kategorie- oder Schlagwortarchiv verschwindet ebenso, sobald es nichts mehr zu zeigen hat; der Begriff selbst bleibt. Dabei behoben: die Beitragsliste fragte je Beitrag einzeln nach Bild und Begriffen – jetzt je eine Abfrage für die ganze Liste, Startseite des Nachbaus 22 → 16 Abfragen.
  • Unterstriche nur noch im Fließtext (ADR 054), dort mit Schalter im Designbereich („Links im Fließtext unterstreichen“, Standard an). Karten, Menü, Kontaktzeilen, Rechtszeile und der „Ansehen“-Streifen über Kartenbildern haben keinen mehr; beim Überfahren erscheint er überall. Die aktuelle Seite im Menü wird über das Schriftgewicht kenntlich – die Akzentfarbe fiel im dunklen Fußmenü durch die Kontrastprüfung.
  • Eigenes Symbol der Website (ADR 055): ein Bild aus der Mediathek in den Einstellungen wählen, beim Speichern entstehen daraus quadratische PNG-Dateien in 32 und 180 px (/favicon.png, /apple-touch-icon.png). Ohne Auswahl bleibt es beim mitgelieferten Zeichen.
  • Die Browserprüfung prüft jetzt jede Installation, nicht nur die Demo: die geprüften Seiten kommen aus der Sitemap, Tastatur- und Ohne-JavaScript-Weg folgen dem ersten Verweis im Inhalt statt festen Linktexten der Demo. Das Ladebudget trennt Bilder (Inhalt) vom Beiwerk (≤ 12 Anfragen) und deckelt stattdessen das übertragene Gewicht (1,5 MB); Skripte werden gegen die Richtlinie der Seite gezählt, strukturierte Daten zählen nicht mit. Der Nachbau von kant-it.de besteht die vollständige Prüfung ohne Befund.
  • Einblendungen laufen beim Scrollen (ADR 052): animation-timeline: view() hinter Bewegungsschranke und @supports; auf dem Scrollweg nur Bewegung, keine Deckkraft, damit Text außerhalb des Blicks lesbar bleibt. Die drei Beitragsvorlagen bringen sie mit.
  • Vier Darstellungen der Beitragsliste (ADR 051): Karten, Liste, Kompakt und Wechselseitig – am Baustein einstellbar, dieselben Daten, nur andere Regeln.
  • Drei Aufbauten für neue Beiträge (ADR 050): „Bildkopf“, „Klassisch“ und „Magazin“, wählbar beim Anlegen. Startpunkte, keine Schablonen – jeder Block bleibt änderbar.
  • Dokumentschema v18 (ADR 049): Baustein „Beitragsnavigation“ – Vor/Zurück zwischen Beiträgen derselben Kategorie, vom Renderer gefüllt, ohne Umbruch am Rand. Dazu Autorotation für den Slider (Sekunden, eigenes Skript nur auf Seiten mit einem solchen Slider) und Blättern in der Beitragsliste.
  • Dokumentschema v17 (ADR 043): eine Beitragsliste kann Datum, Auszug und Kategorie ausblenden und damit als Projektraster erscheinen; ein Bild kann verlinken (Logo auf die Startseite). Dazu die Einstellung „Kopfbereich beim Scrollen oben halten“. Behoben: im Editor ließ sich in Dokumenten ab v13 kein Baustein mehr einfügen.
  • Die Gestaltung färbt jetzt auch die Bausteine (ADR 042): Symbole, Knöpfe, Rahmen, ruhiger Text, Fokusring und Einwilligungsdialog nehmen die Farben des Themes statt fester Werte aus site.css. Der Einwilligungsdialog lässt sich zusätzlich eigens färben über eine Palettenfarbe mit dem Schlüssel consent. Überschriften werden nicht mehr getrennt.
  • Installer (Arbeitspaket N, ADR 041): /install führt in sechs Schritten von einer leeren Installation zu einer Website – Systemprüfung, Datenbank (SQLite oder MySQL, Verbindung sofort geprüft), Migration im Prozess ohne Konsole, Website mit Startseite, Kopf-/Fußbereich, Menü sowie Impressum und Datenschutzgerüst, erstes Administratorkonto, Abschlussliste. Solange keine Installation existiert, führt jede Adresse dorthin; sobald ein Konto existiert, antwortet /install mit 404. Davor liegt scripts/install.php: eine einzelne Datei, die das Releasearchiv mit fester Adresse und fester SHA-256 lädt, gegen ZIP-Slip gesichert entpackt und sich danach selbst löscht.
  • Fassungsverlauf, Einsendungen und Weiterleitungen blättern statt bei 30, 50 beziehungsweise 2000 Einträgen stillschweigend aufzuhören.
  • Sicherung und Wiederherstellung (ADR 038, Arbeitspaket O): cms:backup schreibt Datenbank (über VACUUM INTO, nicht als Dateikopie) und Medienordner nach var/backups/, mit Manifest samt Prüfsumme, und rotiert die alten. In der Verwaltung gibt es die Übersicht mit Zustand je Sicherung und einen Knopf zum Anlegen. Zurückspielen nur auf der Konsole (cms:restore): Prüfsumme, Fassungsvergleich, Sicherheitskopie des aktuellen Stands, Wartungszustand, Datenbank ersetzen, Medien ergänzen statt ersetzen. Nicht enthalten: .env, vendor/, Quellcode – steht so im Manifest.
  • Seiten ohne Menü, andere Köpfe je Seite (ADR 037, Dokumentschema v16): jede Seite wählt Kopf- und Fußbereich – Standard, ein anderer Bereich oder keiner, für eine Landingpage ohne Navigation. Dafür sind mehrere Kopf- und Fußbereiche erlaubt, jeder mit eigener Adresse; der erste bleibt default. Ein Verweis auf einen gelöschten Bereich fällt auf den Standard zurück statt die Navigation stillschweigend zu verlieren.
  • Vier Menüplätze statt zwei (Hauptnavigation, zweite Navigation, Fußzeile, Rechtliches) und drei Menüarten: waagerecht, untereinander, aufklappbar als Burger. Der Burger ist ein details-Element und öffnet ohne JavaScript.
  • Die globale Seitenbreite wirkt jetzt auch nach oben. containerWidth im Designbereich gab es, aber site.css deckelte bei 1180 px – schmaler ging, breiter nicht. Dritter Fehler dieser Art an einem Tag: eine Einstellung, die sich speichern ließ und nichts tat.
  • Vollflächige Bilder und volle Bildschirmhöhe: Abschnitte können randlos sein (ohne Begrenzung, ohne Rand, von Kante zu Kante) oder breit (1600 px). Für die Höhe gibt es jetzt die Einheiten dvh, svh, lvh – 100vh ist auf dem Handy höher als der Bildschirm, 100dvh ist das, was gemeint ist. Die Vorlage „Vollbild-Hero“ setzt die ganze Kombination auf einen Klick: randlos, 100dvh (mobil 80dvh), Inhalt senkrecht zentriert, Hintergrundbild formatfüllend, dunkle Überlagerung, Überschrift, Text, Button.
  • Designer ausgebaut (ADR 036, Dokumentschema v15): 93 Gestaltungseigenschaften statt 50, je Gerät und teils für Hover. Neu: Bild-Eigenschaften, die auf das Bild im Block wirken (Ausschnitt, Bildmitte, Seitenverhältnis, Höhe, Ecken, Rahmen), Positionierung mit z-index, getrennte Zeilen- und Spaltenabstände, Flex-Eigenschaften, Innen- und Textschatten, acht Bildfilter, Mischmodus, Zusatzrahmen. Neun Einblendungen mit Verzögerung, sieben Hover-Effekte, alle innerhalb von prefers-reduced-motion. Drei neue Bausteine: Symbol, Fortschritt (mit role="meter") und Slider als Scrollbereich mit Einrasten – ohne JavaScript, ohne Autoplay. „Zeile, 2/3/4 Spalten“ legt eine Zeile samt Spalten auf einen Klick an. Der Vergleich mit Flatsome steht in docs/DESIGNER-VERGLEICH.md, mit dem was fehlt und warum.
  • Zwei Fehler dabei gefunden und behoben: Rahmenstärken je Seite erzeugten border-width-top – kein gültiges CSS, vom Browser stillschweigend verworfen, also ohne jede Wirkung. Und eine Rundung auf einem Bildblock rundete den Rahmen, nicht das Bild.
  • Update über Git (ADR 035, Arbeitspaket N/UPDATE.md): cms:update und ein Bildschirm „System“ zeigen Fassung, Commit, verfügbaren Tag und eine Vorprüfung, die jede Bedingung einzeln nennt – unter anderem, ob Datenbank und Medienordner von Git ausgenommen sind. Der Ablauf: sichern, Wartungszustand (503 mit Retry-After, /admin bleibt offen), Checkout des Tags, Fassung prüfen, Composer, Migration, Cache, Rauchtest als eigener Prozess (cms:health). Scheitert ein Schritt, wird auf den vorherigen Commit zurückgesetzt, die Datenbank nur dann zurückgeschrieben, wenn die Migration schon begonnen hatte – und wenn der alte Stand antwortet, geht die Website wieder online. --check, --dry-run, --to=v1.2.0 und --maintenance=an|aus gibt es auf der Konsole.
  • Statistik (ADR 034, Arbeitspaket L): Aufrufe je Tag und Stunde, meistaufgerufene Seiten, Verweise von außen und nicht gefundene Adressen, als CSV exportierbar. Gespeichert wird ein Zähler je (Tag, Stunde, Pfad, Status, Verweis-Domain), kein Datensatz je Aufruf – keine IP-Adresse, keine Kennung, kein Cookie, auch nicht gehasht. Deshalb braucht diese Messung keine Einwilligung. Diagramme zeichnet der Server als SVG, ohne Skript. Was sie nicht kann, steht auf dem Bildschirm: Aufrufe statt Besucher, keine Verweildauer, keine Absprungrate, kein Klickweg, kein Land. Nach 60 Tagen werden Stunden zu Tageswerten verschmolzen, nach 425 Tagen gelöscht (cms:stats:prune).
  • Einwilligung (ADR 032, Dokumentschema v14): serverseitig gerendertes Banner ohne JavaScript, „Alle akzeptieren“ und „Nur notwendige“ gleich groß nebeneinander, Auswahl je Gruppe im Aufklappfeld. Die Entscheidung steht in einem eigenen Cookie – keine Kennung, kein Serverabgleich, ein halbes Jahr. Unter /einwilligung jederzeit änderbar, von jeder Seite verlinkt. Der neue Block „Externer Inhalt“ lädt ohne Einwilligung nichts: kein iframe, keine Adresse im Markup, frame-src 'none'. Der Platzhalter nennt den Anbieter und gewährt auf Knopfdruck genau diese Gruppe. Ist der Hinweis abgeschaltet, laden die Inhalte – sonst würde das Abschalten jede Seite stillschweigend leeren.
  • Datenschutz und Impressum sind Pflicht, nicht Einstellung (ADR 033): fehlen sie, warnt die Verwaltung und legt sie auf Knopfdruck als Gerüst mit NOCH AUSZUFÜLLEN-Markierungen an. Verlinkt werden sie aus dem Template, nicht aus dem Fußbereich, damit der Link nicht versehentlich gelöscht werden kann.
  • Kopf- und Fußbereich lagen falsch im Layout. Umut hat es im Screenshot gesehen: die Wortmarke stand als Buchstabenkolonne in einem 60 Pixel breiten Kasten. Zwei Ursachen, beide behoben – .site-header war noch ein Flexcontainer für das alte, handgeschriebene Markup und quetschte die Sektion darin zusammen, und design.css band alle Designklassen an #main, sodass Kopf- und Fußbereich außerhalb davon jede Designeinstellung ignorierten.
  • Benutzerverwaltung (ADR 030): Konten anlegen, Rolle ändern, Passwort setzen, Konto löschen – nur für Administratoren. Eine Rolle je Konto (Administrator, Veröffentlicher, Redakteur). Ein Passwort oder eine Rolle zu ändern meldet jede offene Sitzung dieses Kontos ab, weil die Sitzung Hash und Rollen gegen die Datenbank prüft; nur die Sitzung, die ihr eigenes Passwort ändert, bleibt angemeldet. Niemand kann sein eigenes Konto löschen oder herabstufen, deshalb bleibt immer ein Administrator übrig. Das eigene Passwort darf jeder ändern, aber nur mit dem aktuellen. Die Liste zeigt letzte Anmeldung, Passwortdatum und Anlagedatum.
  • Mediathek als Seite (ADR 031): /admin/media zeigt die Bilder direkt, 24 je Seite, mit Blättern und Archivfilter. Hochladen und Bildangaben über gewöhnliche Formulare, ohne JavaScript. Hochgeladen wird über eine Ablagefläche: Bilder hineinziehen oder klicken; darunter steht, welche Dateien gewählt sind und welche übersprungen wurden. Darunter liegt ein echtes, per Tastatur erreichbares Dateifeld – ohne JavaScript bleibt es der Dateidialog, und abgeschickt wird immer erst auf Knopfdruck. Der Dialog bleibt für die Bildauswahl im Editor.
  • Verwaltung neu gestaltet (ADR 029): ein Rahmen für alle Bildschirme mit fester Seitenleiste, aktivem Eintrag über aria-current, Titel, einem Satz Erklärung und Platz für die Hauptaktion. Vorher trug jede Seite ihre eigene Kopfzeile und eine eigene, unvollständige Linkliste. Je Seite eine Zeile mit dem Alltäglichen und einem Aufklappfeld „Einstellungen“, das an seinem Platz öffnet, statt sechs Kästen, die über den Rand hinausragten. Tabellen scrollen seitlich, statt die Karte aus dem Fenster zu schieben. Die Editorregeln stehen jetzt in editor.css, das nur Editor und Mediathek laden.
  • Verwaltung neu gestaltet (ADR 029): ein Rahmen für alle Bildschirme mit fester Seitenleiste, aktivem Eintrag über aria-current, Titel, einem Satz Erklärung und Platz für die Hauptaktion. Vorher trug jede Seite ihre eigene Kopfzeile und eine eigene, unvollständige Linkliste. Je Seite eine Zeile mit dem Alltäglichen und einem Aufklappfeld „Einstellungen“, das an seinem Platz öffnet, statt sechs Kästen, die über den Rand hinausragten. Tabellen scrollen seitlich. Der Editor behält seinen Aufbau und übernimmt die neue Gestaltung; seine Regeln stehen jetzt in editor.css, das nur Editor und Mediathek laden.
  • Eigenes CSS je Element (Dokumentschema v13) im neuen Reiter „Erweitert“: Eigenschaften für das Element und verschachtelte Regeln mit &. Der Selektor wird vom Server gesetzt, die Ausgabe neu serialisiert – eine überzählige } kann den Block nicht verlassen. Abgewiesen: @-Regeln, Backslash-Escapes, expression(), fremde Selektoren und jede Adresse außer einem einzelnen / oder https://. Für jeden mit Editorrolle.
  • Eigenes Skript je Element nur für Administratoren. Durchgesetzt beim Schreiben durch Vergleich mit dem gespeicherten Stand – ein Editor kann eine Seite mit Skript weiter bearbeiten, das Skript aber nicht anfassen, auch nicht über eine eigene Anfrage. Ausgeliefert als /assets/seite/{id}.js nur für veröffentlichte Seiten, jedes Skript in einer eigenen Closure mit element und try/catch. Nur eine Seite mit eigenem Skript sendet script-src 'self', jede andere behält script-src 'none'; inline ist nirgends erlaubt. In der Editorvorschau wird nichts ausgeführt.
  • Formatierter Text in Textblöcken (Dokumentschema v12): fett, kursiv, unterstrichen, durchgestrichen, Code, hervorgehoben, hoch-/tiefgestellt, Absätze, Listen und Links. Im Editor eine Werkzeugleiste plus HTML-Ansicht; eingefügter Inhalt kommt als reiner Text an. Gefiltert wird auf dem Server beim Speichern, nicht im Editor und nicht beim Rendern – was in der Datenbank liegt, ist schon sicher. Nicht erlaubte Elemente werden ausgepackt, der Text bleibt; script, style, iframe und Verwandte werden samt Inhalt entfernt. Attribute fallen weg außer href nach Adressprüfung. Ältere Dokumente behalten Klartextsemantik und werden weiter maskiert.
  • Kopf- und Fußbereich sind Blockdokumente unter /admin/partials, im selben Editor gestaltbar wie eine Seite. Im Template steht keine Marke und keine Navigation mehr; ein neuer Block menu zeigt einen Menüplatz. Geteilte Bereiche haben keinen Entwurf und keine Veröffentlichung – gespeichert heißt live – und dürfen keine Hauptüberschrift enthalten, weil jede Seite ihre eigene bringt.
  • Wiederverwendbare Sektionen werden verknüpft, nicht kopiert (Block partialref, Dokumentschema v11): die Seite speichert nur die Adresse, eine Änderung erreicht jede Seite, die sie einbindet. Zyklen werden abgewiesen, Verschachtelung auf drei Ebenen begrenzt, eine entfernte Sektion hinterlässt keine Lücke und der Verweis bleibt gespeichert. Die Abschnittsvorlagen kopieren weiterhin – beide Wege stehen bewusst nebeneinander.
  • Frei wählbare Startseite unter Einstellungen. Es gibt jetzt eine Quelle dafür – SiteSettings::homePath() und publicPath(); das Literal home kommt im Anwendungscode nicht mehr vor. Die gewählte Seite muss veröffentlicht und nicht im Papierkorb sein, ihre eigene Adresse leitet dauerhaft auf /, und solange sie die Wurzel ist, lässt sie sich nicht umbenennen oder löschen.
  • /sitemap.xml, /robots.txt, /llms.txt und /feed.xml waren lokal nie erreichbar, obwohl jeder Kernel-Test sie bestand: der eingebaute PHP-Server antwortet für fehlende Pfade mit Dateiendung selbst mit 404. bin/dev-router.php bildet die Rewrite-Regel des Hostings nach; Start- und CI-Befehl nutzen es, und der Browsertest ruft die vier Adressen jetzt über HTTP auf.
  • Barrierefreiheitsbefunde werden nach Ursache getrennt: ein Kontrastfehler aus Theme oder Stylesheet bricht den Lauf ab, einer aus einer im Editor gesetzten Blockfarbe wird bei jedem Lauf mit Seite, Farbe und Verhältnis ausgegeben und in reports/browser.json geschrieben. Stillschweigend durchgelassen wird nichts.
  • Seiten umbenennen und verschieben zieht den ganzen Teilbaum mit und hinterlässt für jede alte Adresse eine dauerhafte Weiterleitung. Kollisionen, Tiefengrenze und das Verschieben unter eigene Unterseiten werden vorher abgewiesen; alles in einer Transaktion.
  • Papierkorb statt sofortigem Löschen für die Editorrolle: alle Fassungen bleiben, öffentlich 404, verschwunden aus Navigation, Archiven, Feed, Sitemap, llms.txt und Editorliste. Unterseiten gehen mit; beim Wiederherstellen muss das Elternteil zuerst zurück. Endgültiges Löschen bleibt der Administratorrolle.
  • Fassungsverlauf je Seite unter /admin/pages/{id}/revisions mit Wiederherstellen. Das überschreibt nichts: die alte Fassung wird als neuer Entwurf gespeichert.
  • revision.seq behebt einen echten Fehler: die Reihenfolge des Verlaufs war nicht eindeutig, weil created_at nur Sekunden kennt und mehrere Speichervorgänge in dieselbe Sekunde fallen. Jede Fassung hat jetzt einen je Seite aufsteigenden Zähler.
  • Geplante Veröffentlichung über publish_at und den Cron-Befehl cms:publish-due. Ohne Cron bleibt die Seite ein Entwurf. Zeiten in UTC; Seiten im Papierkorb werden nie durch den Zeitplan veröffentlicht.
  • Die erzeugten Stile wirkten in der Editorvorschau nicht. Ein srcdoc-Iframe erbt die Richtlinie der Verwaltung, die Inline-Styles verbietet; das Stylesheet wurde stillschweigend verworfen. Die Vorschau liefert es jetzt als inertes <template>, der Editor wendet es über adoptedStyleSheets an. Ausgeliefert wurde es die ganze Zeit korrekt – nur die Vorschau log.
  • Die Einrückung der Unterseiten in der Seitenliste hat nie funktioniert: sie lief über ein style-Attribut, das dieselbe Richtlinie verwirft. Jetzt über Klassen.
  • Das Theme überschrieb alle Linkfarben und erzeugte damit 3,08:1 auf dunklem Grund. Die Regel gilt jetzt nur für Links in Textblöcken; die voreingestellte Linkfarbe #a4510f liegt auf allen hellen Tönen über 4,5:1.
  • Bewegungseffekte (Schatten, Hover, Einblendung) bleiben als eigener Panelabschnitt für jeden Block: sie sind Keyframes und lassen sich mit freien Deklarationen nicht ausdrücken. Die alten Tokenwerte erscheinen nur noch, wo eine Seite sie trägt.
  • Direktbearbeitung in der Vorschau: Doppelklick auf Überschrift, Text, Zitat, Aufruf-Band, Symbolkarte, Akkordeon-Eintrag, Tab-Titel oder Kennzahl bearbeitet den Text an Ort und Stelle; Escape beendet, Eingaben werden zu einem Undo-Schritt zusammengefasst. Die Vorschau führt dabei weiterhin kein eigenes JavaScript aus – der Editor greift in ihr Dokument. Eine Schaltfläche im Panel startet dasselbe ohne Maus.
  • Kontrastwarnung im Designpanel: Verhältnis zwischen wirksamer Text- und Hintergrundfarbe nach WCAG 2.1 mit drei Stufen. Eine Warnung, kein Verbot.
  • Gestaltungsvorlagen im Theme: eine Gestaltung unter einem Namen speichern und auf jedem Block jeder Seite anwenden, dazu Kopieren und Einfügen zwischen Blöcken innerhalb der Sitzung. Vorlagen werden mit demselben Compiler geprüft wie Blockstile.
  • Hintergrundbilder werden über die Mediathek gewählt statt über eine eingetippte ID.
  • Freie Gestaltung (Dokumentschema v10). StyleSchema erklärt jede Eigenschaft einmal mit Typ, CSS-Entsprechung und Grenzen; Validator, CSS-Erzeugung und Editorpanel lesen dieselbe Definition. Rund fünfzig Eigenschaften in sieben Gruppen: Typografie (Schriftart, Größe, Stärke, Lage, Zeilenhöhe, Buchstaben- und Wortabstand, Ausrichtung, Schreibweise, Textlinie, Farbe), Hintergrund (Farbe, Verlauf, Bild mit Größe/Ausschnitt/Wiederholung/Parallax, Überlagerung), Abstände je Seite, Größen, Rahmen je Seite und Ecken je Ecke, Layout (Flex, Raster, Verteilung, Abstand, Reihenfolge) und Wirkung (Deckkraft, freier Schatten, Verschieben, Skalieren, Drehen, Übergangsdauer). Werte je Gerät mit Vererbung Desktop → Tablet → Mobil und einzeln zurücksetzbar; farbige, numerische und Bewegungswerte zusätzlich für den Hover-Zustand.
  • Theme unter /admin/theme: frei definierbare Farbpalette (bis 40 eigene Farben, im Editor als @schlüssel), Schriften aus Systemschrift oder Google Fonts mit Auswahl der Schnitte, und die Grundtypografie aus Schrift, Grundgröße, Zeilenhöhe, Überschriftenskala, Inhaltsbreite sowie Text-, Hintergrund- und Linkfarbe. Eine Palettenfarbe zu ändern wirkt überall, wo sie verwendet wird.
  • Der Server erzeugt pro Seite ein blockgenaues Stylesheet mit Media Queries und :hover,:focus-within. Es steht inline und ist über seinen eigenen SHA-256-Hash erlaubt, nicht über unsafe-inline. Google Fonts auf ausdrückliche Nutzerentscheidung – das bedeutet eine Anfrage an Google je Seitenaufruf; die Richtlinie öffnet die Hosts nur, wenn tatsächlich eine Google-Schrift eingestellt ist.
  • SEO: Indexierungssteuerung und Vorschaubild je Seite, Unternehmensdaten unter /admin/settings, sitemap.xml, robots.txt, Weiterleitungen mit 301 und 410, optionale llms.txt, Brotkrumen-Block (Dokumentschema v9) sowie strukturierte Daten für Organization/LocalBusiness, Article und BreadcrumbList. Alles Maschinenlesbare wird aus dem veröffentlichten Bestand abgeleitet: eine nicht indexierbare Seite verschwindet gleichzeitig aus der Sitemap und bekommt ein noindex-Meta. Ohne gepflegte Unternehmensdaten wird kein leerer Organization-Block ausgegeben. llms.txt ist standardmäßig aus.
  • Dokumentschema v8: Formulare. Verwaltung unter /admin/forms mit Feldtypen Text, E-Mail, Telefon, Mehrzeiliges, Auswahl und Einwilligung, Empfänger, Betreff, Antwortadresse und Erfolgsmeldung. Der Formularblock im Seiteninhalt enthält nur die Adresse – Felder und Empfänger liegen in der Datenbank, damit gespeicherter Inhalt nicht ändern kann, wohin eine Anfrage geht. Serverseitige Validierung mit einer Meldung je Feld über aria-describedby/aria-invalid, Eingaben bleiben bei Ablehnung erhalten (HTTP 422), gültige Absendung per HTTP 303 zurück auf die Seite. Spamschutz ohne Dienst Dritter: Honeypot, signierter Zeitstempel und fünf Absendungen je Absender und Stunde. Die Absenderadresse wird nie gespeichert, nur ein Hash; der Zeitpunkt der Einwilligung getrennt. Gespeichert wird vor dem Versand, damit ein kaputter Mailversand keine Anfrage verliert; nicht versendete Einsendungen sind in der Verwaltung gekennzeichnet. Voreingestellter Transport sendmail, per MAILER_DSN auf SMTP umstellbar. Kein CSRF-Token auf dem öffentlichen Weg, weil dafür eine Sitzung und damit ein Cookie auf jeder öffentlichen Seite nötig wäre; Begründung in ADR 017.
  • Dokumentschema v7: Tabs. Der Server liefert Ankerliste und alle Panels mit Überschriften, also vollständigen Inhalt ohne JavaScript; public/assets/tabs.js macht daraus ein echtes Tab-Widget mit tablist/tab/tabpanel und Pfeiltastenbedienung. Die strenge Richtlinie bleibt Voreinstellung: öffentliche Seiten senden weiterhin script-src 'none', nur eine Seite mit Tab-Block sendet script-src 'self'. Inline-Skript ist nie erlaubt. In der Editorvorschau erscheinen Tabs untereinander, weil die Vorschau kein JavaScript ausführt; ein Hinweis im Eigenschaftenpanel sagt das.
  • Dokumentschema v6 mit zehn Blöcken für die Pilotseite: Symbolkarte, Kennzahl, Kundenstimme mit Bewertung, Logoleiste, Aufruf-Band, Kontaktdaten, Trennlinie, Abstand, Akkordeon und Akkordeon-Eintrag. Symbole als Inline-SVG aus einer festen Liste, kein Symbolschriftsatz und keine externe Anfrage. Kontaktdaten werden zu tel:- und mailto:-Verweisen. Akkordeon über details/summary, also ohne öffentliches JavaScript. Tabs sind bewusst nicht umgesetzt – ohne JavaScript gibt es dafür keine zugängliche Lösung; Begründung in ADR 015.
  • Beiträge: Datum, Autor, Auszug und Beitragsbild als Seitenspalten, gepflegt über ein Formular in der Seitenübersicht. Kategorien und Schlagwörter als Begriffe mit eigenen Archiven unter /kategorie/{slug} und /schlagwort/{slug}, Gesamtübersicht unter /beitraege mit Paginierung über ?seite=N, RSS unter /feed.xml. Der Block postlist (Dokumentschema v5) speichert nur Anzahl und Filter; die Beiträge stellt der Controller beim Ausliefern zusammen, damit ein gespeichertes Dokument keinen unveröffentlichten Beitrag zeigen kann.
  • Menüverwaltung unter /admin/menus für die Plätze Hauptnavigation und Fußzeile. Ein Eintrag verweist auf eine Seite, auf ein Sprungziel innerhalb einer Seite oder auf eine Adresse (HTTPS, lokaler Pfad, mailto:, tel:). Zwei Ebenen, bis 60 Einträge, optimistische Konflikterkennung. Bedienung über eine nummerierte Zeilenliste, vollständig ohne JavaScript; Untermenüs in der Ausgabe über details/summary. Seitenverweise speichern die Seiten-ID, nicht den Pfad – ein Eintrag folgt einer umbenannten Seite und überlebt eine gelöschte.
  • Dokumentformat v4: Blocktypen mit Namensraum (acme.slider) gelten als Bausteine einer Erweiterung und werden erhalten statt abgewiesen. Öffentlich rendert der Kern aus ihnen nichts außer einem HTML-Kommentar, in der Editorvorschau einen Platzhalter. Im Editor bleiben sie auswählbar, verschiebbar, duplizierbar und löschbar, aber nicht bearbeitbar. v1 bis v3 bleiben unverändert unterstützt.
  • Login-Throttling nutzt in der Testumgebung einen Array-Cache. Zuvor überdauerten die Zähler den Testlauf und sperrten die Anmeldung nach wenigen Wiederholungen aus, was als Testfehler erschien.
  • Lokale PHP-/Browser-/Lighthouse-Prüfungen und minimale GitHub-Actions-Konfiguration einschließlich vorbereitetem MySQL-Smokejob.

Geprüft

  • 201 PHP-Tests / 716 Assertions unter PHP 8.2.12 und 8.4.22 erfolgreich; zusätzlich 15 JavaScript-Modelltests, der vollständige Playwright-Editortest und die öffentliche Browserprüfung. Abgedeckt sind Migration/Erhaltung, Versionen, ungültige Geräte-/Typ-/Design-/Effektwerte, Vererbung/Reset/Undo, Vorlage-/Galerie-IDs, Uploadvalidierung und Neuencodierung, Metadatenkonflikte, Archivierung sowie sichere Bildquellen.
  • Neu geprüft (15 Tests): privater Entwurf nach dem Anlegen, verschachtelte Pfade mit gleichem Slug unter verschiedenen Eltern, abgewiesene Adressen (Großbuchstaben, Schrägstrich, leer, reserviert, unbekannter Typ, fehlendes Elternteil) ohne Schreibzugriff, doppelte Adresse, Tiefengrenze, Beiträge ohne Unterseiten, Löschen mit allen Revisionen samt Schutz für Elternseiten und veraltete Versionen, sowie dass die CMS-Routen nicht vom öffentlichen Auffangmuster verschluckt werden. Über HTTP zusätzlich: anonyme Schreibversuche, CSRF für Anlegen und Löschen, Adressableitung aus dem Titel, kein Löschangebot und HTTP 403 für die Editorrolle, erfolgreiches Löschen als Administrator.
  • Verwaltung geprüft: axe ohne Verstöße auf Anmeldung, Seitenübersicht und Editor (Desktop und Mobil), 201 PHP-Tests grün. Nicht geprüft: kein Screenreader, und die Bildschirme Menüs, Formulare, Bereiche, Design, Einstellungen und Fassungen sind nur von Hand angesehen, nicht automatisiert auf Barrierefreiheit geprüft – die Browserprüfung deckt sie nicht ab.
  • Aufgabenplan geprüft (14.09.2026): 12 Tests auf die Mechanik (nur Fälliges läuft; eine gesperrte Aufgabe wird übergangen, eine abgelaufene Sperre übernommen; ein Fehlschlag wird festgehalten und der nächste Versuch rückt weiter weg; eine abgeschaltete Aufgabe läuft nicht, von Hand gestartet doch; eine Anfrage arbeitet nicht alles auf einmal ab; eine fehlende Tabelle ist kein Fehler) und 5 über HTTP (der Bildschirm nennt jede Aufgabe und die Adresse des URL-Cronjobs; anonym führt niemand etwas aus, eine Redakteurin bekommt 403, ohne gültiges Formular passiert nichts; der Takt des Backends antwortet mit JSON; ein falscher Cron-Schlüssel bekommt 404). Von Hand auf der Entwicklungsinstallation alle vier Auslöser ausgeführt: alle fünf Aufgaben über die Konsole (Sicherung 36 ms, Updateprüfung 318 ms gegen das echte Repository), ein Seitenaufruf hat eine fällige Aufgabe abgearbeitet (Auslöser besuch im Protokoll), das geöffnete Backend hat getaktet (POST /admin/aufgaben/takt → 200), und der URL-Cronjob hat mit dem richtigen Schlüssel Text geliefert und mit einem falschen 404. Der Bildschirm wurde im Browser bedient (Abstand und Schalter gespeichert und wieder zurückgesetzt); dabei fiel eine Inline-Stilangabe auf, die die CSP der Verwaltung blockiert hat – sie ist jetzt eine Klasse.
  • Ladeverhalten geprüft: drei Tests – 30 Fassungen ergeben zwei Seiten mit richtiger Reihenfolge und richtigem Rest, eine Seite jenseits des Endes zeigt die letzte statt einer leeren; das erste Bild trägt loading="eager" und fetchpriority="high", das zweite lazy; und die Startseite bleibt unter zwölf Abfragen. Dazu das Ladebudget in der Browserprüfung.
  • Wiederherstellung nachgewiesen, nicht nur gebaut – das war der älteste offene Punkt aus Arbeitspaket I. Acht Tests gegen eine wegwerfbare Installation, darunter der eigentliche: Sicherung anlegen, Seite und Bilddatei löschen, zurückspielen, beides ist wieder da. Dazu von Hand auf der Entwicklungsinstallation: Testseite angelegt (5 Seiten), gesichert, gelöscht (4), zurückgespielt (5, Seite mit ihrer Adresse wieder da), Testseite entfernt (4). Ebenfalls geprüft: eine manipulierte Sicherung wird abgewiesen, eine aus einer neueren Fassung nur mit --force, Rotation behält die neuesten, und eine nach der Sicherung hochgeladene Datei überlebt die Wiederherstellung.
  • Seitenbereiche geprüft: Standard, anderer Bereich, „keiner“ (dann steht weder site-header noch site-footer im HTML, die Seite selbst aber schon), ein Verweis auf einen gelöschten Bereich fällt auf den Standard zurück, eine veraltete Fassungsnummer und eine ungültige Adresse werden abgewiesen. Menüarten: drei angenommen, eine erfundene abgewiesen, Burger als details – und die Seite behält script-src 'none'. Seitenbreite: die Regel in site.css nimmt den Theme-Wert, und ein größerer Wert erscheint als --k-container im Seiten-Stylesheet.
  • Vollbild geprüft: vier Abschnittsbreiten werden angenommen und eine erfundene abgewiesen; sieben Höhenangaben, darunter dvh, svh und lvh, und zwei ungültige; ein randloser Abschnitt mit formatfüllendem Bild erzeugt #id{width:100%} und #id img{object-fit:cover;height:100dvh}. Die Hero-Vorlage ist im JavaScript-Modelltest nachgerechnet, Eigenschaft für Eigenschaft.
  • Designer geprüft: 16 Datensätze prüfen, dass jede Eigenschaftsform genau die versprochene Deklaration erzeugt – Rahmen je Seite in der richtigen Schreibweise, Ecken, Positionierung, Flex, getrennte Abstände, Innenschatten, Textschatten, Filter in fester Reihenfolge, Mischmodus, und die Bild-Eigenschaften in einer zweiten Regel #id img{…}. Jeder davon muss außerdem den Validator überleben, sonst bietet der Editor etwas an, das nicht gespeichert werden kann. Ein Test hält Schema und Editorpanel zusammen: eine Gruppe, die das Panel nicht kennt, würde ihre Eigenschaften in einem unbenannten Aufklappfeld verstecken.
  • Neue Bausteine geprüft: vier Darstellungen (dekoratives Symbol vor Screenreadern verborgen, sprechendes Symbol mit Text, Fortschritt mit role="meter" und Wert im Markup, Slider mit Klasse je Gerät) und acht abgewiesene Eingaben (Symbolgröße außerhalb der Liste, unbekanntes Symbol, sprechendes Symbol ohne Beschriftung, dekoratives mit Beschriftung, Fortschritt über 100, Fortschritt als Text, Slider mit sieben Folien, Slider ohne Gerätangabe). Keiner der drei braucht ein Skript: die Seite behält script-src 'none'.
  • Updater geprüft: sechs Tests gegen echte Git-Repositorys in einem Temp-Verzeichnis – Vorprüfung benennt geänderte Dateien und eine nicht ausgenommene Datenbank, ein Update wechselt den Tag in der geprüften Reihenfolge und hebt den Wartungszustand auf, ein gescheiterter Schritt setzt Code und Datenbank zurück und bringt die Website auf dem alten Stand wieder online, ein Probelauf verändert nichts, ein unbekannter Tag wird beim Checkout abgewiesen, und eine bereits installierte Fassung ist kein Fehler. Zusätzlich am echten Repository durchgespielt (siehe QUALITY).
  • Statistik geprüft: 32 Tests, darunter die Spaltenliste der Tabelle gegen die Zusage „keine Spalte, aus der eine Person hervorgeht“, 14 Anfragearten (Verwaltung, Bild, Stylesheet, Feed, Weiterleitung, POST, Googlebot, Überwachungsdienst, Headless, curl – alle nicht gezählt), sieben Verweisformen (nur der Host bleibt, die eigene Domain gilt nicht als Verweis), sechs Adressformen (Query verworfen, erfundene 404 gesammelt) und das Verschmelzen und Löschen alter Tage. Dazu durch echte Anfragen: fünf Aufrufe werden ein Zähler, ein 404 zählt nicht als Aufruf, Verwaltung und Feed zählen nicht.
  • Einwilligung geprüft: 26 PHP-Tests, darunter sieben Cookiezustände (gewährt, abgelehnt, andere Gruppe, abgelaufen, ältere Fassung der Frage, Unsinn, unbekannte Gruppe) und zehn abgewiesene Einbettungsadressen (http, protokollrelativ, javascript:, data:, mit Port, mit Zugangsdaten, Leerzeichen im Host, ohne Titel, unbekannte Gruppe, zu geringe Höhe). Im Browsertest zusätzlich: Banner erscheint, kein iframe und keine Adresse im Markup, Einwilligen über den Platzhalter lädt den Inhalt, Widerruf hält ihn wieder zurück, axe ohne Verstöße auf Banner und Einwilligungsseite.
  • Verwaltung geprüft: axe ohne Verstöße auf elf Bildschirmen – Anmeldung, Seitenübersicht, Benutzer, eigenes Konto, Mediathek, Menüs, Formulare, Bereiche, Design, Einstellungen und Editor in Desktop und Mobil. Vorher waren es drei.
  • Benutzerverwaltung geprüft: 16 Tests, darunter zwei gleichzeitige Sitzungen, die zeigen, dass ein zurückgesetztes Passwort und eine geänderte Rolle die andere Sitzung abmelden, während die handelnde angemeldet bleibt. Acht abgewiesene Kontoanlagen (zu kurzes Passwort, mehr als 72 Bytes, Nullbyte, drei ungültige Namen, Doppelname, unbekannte Rolle) legen kein Konto an. Jeder Schreibweg braucht ein CSRF-Token und ist ohne Administratorrolle 403.
  • Mediathek geprüft: Upload über das Formular mit zwei Dateien in einem Absenden, CSRF-Pflicht, Bildangaben mit Versionskonflikt, Archivfilter, Blättern bei 26 Bildern (24 auf Seite eins, korrekte Sortierung neueste zuerst, ?seite=99 wird auf die letzte Seite begrenzt) und Rückkehr auf dieselbe Seite nach dem Speichern. Die Ablagefläche ist im Browsertest geprüft (gewählte Dateien erscheinen im echten Dateifeld und werden benannt) und von Hand mit einem echten Drop, bei dem eine Nicht-Bilddatei übersprungen wurde.
  • Updateverfahren geplant, nicht gebaut: docs/UPDATE.md beschreibt, wie eine neue Version über einen Git-Tag gezogen wird – Vorprüfung, Sicherung, Wartungszustand, Checkout, Migration, Rauchtest, Rückweg. Zwei Entscheidungen hängen am Hosting: Composer auf dem Server oder vendor/ im Tag, und ob es überhaupt Shell-Zugriff gibt.
  • Nicht geprüft: kein Screenreader.
  • Eigener Code geprüft: fünf CSS-Formen werden korrekt auf das Element bezogen, darunter & .block-text mit erhaltenem Nachfahren-Leerzeichen. Zehn abgewiesene CSS-Eingaben erreichen kein Dokument: Ausbruch mit }, fremder Selektor, @import, @media, javascript:-Adresse, protokollrelative Adresse, Backslash-Escape, expression(), Eigenschaft ohne Wert und unsinniger Eigenschaftsname. Eigenes CSS erscheint im Seiten-Stylesheet, eigenes Skript nur als Datei mit script-src 'self' ausschließlich auf dieser Seite; eine Seite ohne eigenes Skript behält script-src 'none' und liefert keine Datei. Ein Skript aus einem unveröffentlichten Entwurf wird nicht ausgeliefert. Der Rollenvergleich weist Ändern, Entfernen und Hinzufügen eines Skripts ab und lässt unveränderte Skripte durch.
  • Formatierter Text geprüft: dreizehn Fälle durch den Filter, darunter erlaubte Auszeichnung und Listen, ein script samt Inhalt entfernt und der Text danach erhalten, Ereignisattribut entfernt, class/style entfernt und das Element ausgepackt, javascript:-Link zu Text geworden, Bild mit onerror entfernt, iframe entfernt, unausgeglichene Auszeichnung geschlossen, lokaler Link erhalten, externer Link mit rel="noopener", Klartext maskiert und Umbruch zu <br>. Dazu: gefiltert wird beim Speichern (die Datenbank enthält schon die sichere Fassung), ein nach dem Filtern leerer Textblock wird abgewiesen ohne zu schreiben, ab v12 erscheint Auszeichnung als Auszeichnung in einem div, vor v12 maskiert in einem p, und in einem geteilten Bereich gilt derselbe Filter.
  • Bereiche geprüft: die eingesetzten Kopf- und Fußbereiche erscheinen als gewöhnliche Blöcke, der Navigationsblock zeigt das Menü, und eine Änderung am Kopfbereich erreicht beide Seiten gleichzeitig. Ein leerer Bereich rendert nichts statt einer kaputten Hülle. Verknüpfte Sektion geprüft: auf zwei Seiten sichtbar, die Seitendokumente enthalten nur die Adresse, eine Änderung wirkt an beiden Stellen, usedBy nennt beide Seiten, und nach dem Entfernen bleiben die Seiten heil und der Verweis erhalten. Abgewiesen werden: Hauptüberschrift in einem geteilten Bereich, Sektion mit Selbstbezug, zweiter Kopfbereich und das Löschen von Kopf- oder Fußbereich.
  • Startseite geprüft: eine andere veröffentlichte Seite wird zur Wurzel, antwortet unter /, ihre eigene Adresse leitet mit 301 dorthin, die bisherige Wurzel wird eine gewöhnliche Seite und ist wieder verschiebbar. Sitemap, strukturierte Daten und Menüauflösung folgen derselben Entscheidung. Solange eine Seite die Wurzel ist, werden Umbenennen und Papierkorb abgewiesen. Drei ungültige Wurzelangaben (nicht vorhanden, leer, Großbuchstaben) ändern nichts.
  • Öffentliche Browserprüfung wieder grün, inklusive der vier maschinenlesbaren Adressen über HTTP mit richtigem Inhaltstyp. Sechs Seiten-/Breitenkombinationen ohne Code-Verstoß; die redaktionelle Feststellung zu /grundlagen wird ausgegeben.
  • Lighthouse 13.4.1: je drei Läufe mobil und Desktop, alle fünf Kategorien 100 mit Startseite, Theme, gehashtem Stylesheet und Routerskript.
  • Seitenverwaltung geprüft: Verschieben ändert drei Adressen über zwei Ebenen, jede neue antwortet mit 200 und jede alte mit 301 auf das richtige Ziel, Slugs und Elternbeziehungen der Unterseiten bleiben erhalten. Vier Fälle werden abgewiesen, ohne etwas zu bewegen oder eine Weiterleitung zu erfinden: in die eigene Unterseite, in sich selbst, auf eine vergebene Adresse und mit einem unbrauchbaren Slug; dazu die Startseite. Papierkorb geprüft: Teilbaum geht mit, öffentlich 404, verschwunden aus Navigation, Sitemap, Archiv, Feed und Editorliste, Kind kann nicht vor dem Elternteil zurück, Wiederherstellen stellt nur die gewählte Seite zurück. Fassungen geprüft: Reihenfolge neueste zuerst, Wiederherstellen fügt eine Fassung hinzu statt zu überschreiben, eine fremde Fassung wird abgewiesen. Zeitplan geprüft: ein künftiger Zeitpunkt veröffentlicht nichts, ein vergangener genau einmal, danach ist der Zeitpunkt verbraucht, und eine Seite im Papierkorb wird nie veröffentlicht.
  • Der vollständige Browser-End-to-End-Test läuft wieder und deckt jetzt das neue System ab. npm run test:editor war durch den Umbau kaputt (er steuerte die abgeschafften Auswahllisten) und wurde auf die freien Bedienelemente umgeschrieben: Einheitenfelder, Vererbung über drei Geräte, einzelnes Zurücksetzen, Palettenfarbe, Abstand je Seite, Hover-Regel über den Template-Weg, freie Rasterspalten und die weiterhin vorhandenen Bewegungseffekte. axe meldet null Verstöße auf Login, Seitenliste, Editor in Desktop- und Mobilbreite, Medienauswahl und der bearbeiteten veröffentlichten Seite.
  • Lighthouse 13.4.1 auf der Startseite mit dem neuen Theme-System: je drei Läufe mobil und Desktop, alle fünf Kategorien 100. Das ist die erste Messung seit dem Umbau des Editors.
  • Im laufenden Editor durchgespielt: Doppelklick setzt das Element auf contenteditable="plaintext-only", eine Eingabe landet im Modell (Speicherstatus wechselt auf „Ungespeicherte Änderungen“, der Strukturbaum zeigt den neuen Text), und das Verlassen beendet die Bearbeitung sauber. Die Kontrastwarnung meldete bei #cccccc auf #ffffff korrekt „Kontrast 1.6:1 – zu wenig“. Die Schaltflächen zum Kopieren und Einfügen erscheinen mit richtigem Aktivierungszustand. Die Teständerungen wurden verworfen und nicht gespeichert.
  • Gestaltungsvorlagen über HTTP geprüft: anonymer Zugriff führt zur Anmeldung, CSRF-Token ist Pflicht, ein Stil mit eingeschleuster Deklaration und ein leerer Name werden mit HTTP 422 abgewiesen ohne zu schreiben, Speichern und Entfernen funktionieren, und das Designpanel erhält Vokabular, Palette und Vorlagen aus einer Quelle.
  • Freie Gestaltung geprüft: ein v10-Dokument mit Farben, Abständen je Seite, Ecken je Ecke, Schatten, Hover und eigenem Mobilwert wird unverändert gespeichert und zu blockgenauem CSS samt Media Query und Hover-Regel. Vierzehn abgewiesene Eingaben schreiben nichts: unbekannte Eigenschaft, unbekanntes Gerät, Farbe als Wort, Farbe als Funktion, Länge ohne Einheit, Länge als calc(), ein eingeschleustes ;position:fixed, Auswahlwert außerhalb der Liste, Zahl außerhalb des Bereichs, Seitenwert als Einzelwert, unbekannte Seite, Hover auf einer Eigenschaft ohne Hover, Schriftschlüssel mit Anführungszeichen und Hintergrundbild als Pfad. Zusätzlich geprüft, dass freie Stile Schema v10 verlangen.
  • Sicherheitsrichtlinie geprüft: das erzeugte Stylesheet wird über seinen eigenen SHA-256-Hash erlaubt, unsafe-inline kommt nicht vor und Skripte bleiben aus. Ohne Google-Schrift wird kein fremder Host erlaubt und keiner kontaktiert; mit Google-Schrift erscheinen fonts.googleapis.com in style-src und fonts.gstatic.com in font-src.
  • Theme geprüft: Palettenfarben werden zu CSS-Variablen und normalisiert, die Schriftvariable enthält Familie und Ersatz, Grundgröße, Zeilenhöhe und Inhaltsbreite wirken auf jeder Seite. Acht ungültige Themes schreiben nichts: Farbe als Wort, doppelter Schlüssel, Schriftname als Stapel, Google-Schrift ohne Namen, Grundfarbe ohne Paletteneintrag, Grundgröße ohne Einheit, Zeilenhöhe außerhalb des Bereichs und leere Palette.
  • Im Editormodell geprüft: Vererbung vom größeren Gerät, einzelnes Zurücksetzen, das Entfernen leerer Reste, Kopieren der Gestaltung ohne geteilte Objekte und ein vollständiges Undo auf den Ausgangszustand.
  • SEO geprüft: die Sitemap ist wohlgeformtes XML und enthält weder eine noindex-Seite noch einen Entwurf, dafür Beitragsübersicht und Begriffsarchiv; robots.txt verweist auf sie und sperrt /admin; eine nicht indexierbare Seite trägt zusätzlich ein noindex-Meta. Weiterleitungen antworten vor dem 404 mit 301 beziehungsweise 410, eine vorhandene Seite gewinnt gegen eine gleichnamige Weiterleitung, und Selbstbezug, Kette, Großbuchstaben sowie ein anderer Statuscode werden abgewiesen. Strukturierte Daten entstehen nur aus gepflegten Werten: ohne Angaben kein Block, danach LocalBusiness mit Anschrift und entdoppelten Profilen, Article mit Autor aus den Beitragsdaten und BreadcrumbList aus der Hierarchie. llms.txt antwortet ausgeschaltet mit 404 und listet eingeschaltet weder eine noindex-Seite noch einen Entwurf. Fünf ungültige Unternehmensangaben schreiben nichts, und in robots.txt überleben weder Markup noch eine zweite Sitemap-Zeile.
  • Formulare geprüft: Ausgabe ohne Skript, Honeypot außerhalb der Tabreihenfolge, gültige Absendung wird gespeichert und per HTTP 303 zurückgeleitet, Einwilligungszeitpunkt festgehalten, keine Absenderadresse in der Zeile sondern ein 64-stelliger Hash, Erfolgsmeldung auf der Zielseite. Fehlerhafte Absendung: HTTP 422, nichts gespeichert, vier Meldungen an den richtigen Feldern mit aria-describedby und aria-invalid, genau eine Zusammenfassung mit role=alert, alle Eingaben erhalten. Vier Spamfälle (gefüllter Honeypot, zu schnell, gefälschte Signatur, zu alter Zeitstempel) werden still verworfen und wie ein Erfolg beantwortet. Das Limit stoppt die sechste Absendung innerhalb einer Stunde. Die Formularverwaltung verlangt Anmeldung, Editorrolle und CSRF-Token und weist eine ungültige Empfängeradresse mit Meldung ab.
  • Tabs geprüft: beide Panels samt Inhalt und Überschrift stehen ohne JavaScript im HTML und sind serverseitig nicht versteckt, die Ankerliste verweist auf die Panels, das Skript wird als externe Datei geladen und nie inline. Die Richtlinie sendet script-src 'self' ausschließlich auf der Seite mit Tab-Block; eine Seite ohne Tabs und die 404-Seite behalten script-src 'none' und laden kein Skript. Ein Tab außerhalb einer Tabgruppe, eine Tabgruppe mit Textkind und Tabs in Schema v6 werden abgewiesen. Im Modell: eine neue Tabgruppe bringt zwei Tabs mit eigenen IDs mit, nimmt nur Tabs auf, und ein Tab lässt sich weder in eine Sektion noch in ein Akkordeon einfügen.
  • Neue Blöcke geprüft: eine Seite aus allen zehn Abschnittsarten wird unverändert gespeichert, veröffentlicht und ausgegeben; Kontaktdaten werden zu tel:- und mailto:-Verweisen mit bereinigter Nummer, das Akkordeon ist ein echtes details-Element, Symbole sind Inline-SVG, die Bewertung ist für Screenreader ausgeschrieben, und die Ausgabe enthält kein script. Vierzehn abgewiesene Eingaben schreiben nichts: unbekanntes Symbol, fehlender Titel, javascript:-Ziel, Bewertung außerhalb 0–5 und als Zeichenkette, leerer Kontaktblock, ungültige Telefonnummer und E-Mail-Adresse, unbekannte Abstandsgröße, unerlaubtes Linkschema, Text in Logoleiste und Akkordeon, Akkordeon-Eintrag ohne Akkordeon sowie Kinder an einer Trennlinie. Zusätzlich geprüft, dass die neuen Blöcke in Schema v5 weiterhin abgewiesen werden.
  • Beiträge geprüft: Sortierung nach Datum mit Paginierung und Begrenzung zu großer Seitenzahlen, Seiten und unveröffentlichte Beiträge bleiben aus dem Archiv, Kategorie- und Schlagwortarchive einschließlich getrennter Namensräume, erneutes Speichern ersetzt die Zuordnung statt sie zu ergänzen, ein leer gewordenes Archiv antwortet weiter mit HTTP 200 und leerer Liste, Löschen löst die Zuordnungen und lässt die Begriffe bestehen, Veröffentlichen stempelt ein fehlendes Datum und lässt ein gepflegtes unangetastet, Seiten werden nie gestempelt. Sechs abgewiesene Metadateneingaben schreiben nichts und erhöhen die Version nicht. Die Beitragsliste zeigt nur Beiträge des gefilterten Begriffs, und das gespeicherte Dokument enthält weiterhin ausschließlich Anzahl und Filter. Der Feed ist wohlgeformtes XML mit absoluten Adressen und enthält keinen unveröffentlichten Beitrag.
  • Menüs geprüft: Seiten-, Anker- und externe Ziele einschließlich tel: in der Ausgabe, aria-current nur auf der tatsächlich aktuellen Seite und nicht auf Ankereinträgen, gelöschte und unveröffentlichte Seiten fallen aus der Navigation ohne die übrigen Einträge zu verlieren, Kinder eines übersprungenen Eintrags rücken nach, gespeicherte Einträge bleiben erhalten und erscheinen nach dem Veröffentlichen wieder. Zehn abgewiesene Eingaben (leere und überlange Beschriftung, unbekannte Zielart, fehlende Seite, ungültige Sprungziele, javascript:, http:, protokollrelative Adresse, dritte Ebene) schreiben nichts. Über HTTP zusätzlich: anonymer Schreibversuch, CSRF, veraltete Version, Aufbau des Baums aus flachen Formularzeilen mit Position und Ebene, normalisiertes Sprungziel und die Bestätigung, dass die Navigation nicht mehr aus dem Template stammt.
  • Plugin-Erhalt geprüft: ein v4-Dokument mit acme.slider samt verschachteltem Text wird Byte für Byte gespeichert, veröffentlicht und wieder geladen; weder Props noch Kindinhalte noch eine javascript:-URL erscheinen in der öffentlichen Ausgabe, die übrige Seite rendert normal. Fehlerhafte Namensräume und Plugin-Blöcke in v1 bis v3 werden weiterhin abgewiesen, ohne zu schreiben. Im JavaScript-Modell zusätzlich: Verschieben, Duplizieren mit neuen IDs, unverändertes Undo, kein Einfügen in den Baustein und keine Erzeugung über die Elementbibliothek.
  • Testumgebung: tests/bootstrap.php verwirft var/cache/test einmal pro Lauf. Ohne Debug prüft Symfony weder den kompilierten Container noch die Vorlagen auf Aktualität; das hat zweimal ein falsches Ergebnis erzeugt (eine Vorlagenänderung wurde übergangen, später schlug ein geänderter Konstruktor aus einem veralteten Container fehl). Zusätzlich lädt Twig in der Testumgebung automatisch neu.
  • Migration 20260912000600 zuerst gegen eine Kopie der lokalen Datenbank ausgeführt und das Ergebnis kontrolliert: acht Anweisungen, vorhandene Seiten und Dokumente unverändert, neue Tabellen term und page_term, Sortierindex angelegt. Danach nach Backup unter var/backups/before-posts-*.db lokal angewendet.
  • Migration 20260912000500 zuerst gegen eine Kopie der lokalen Datenbank ausgeführt, dann nach Backup unter var/backups/before-menus-*.db lokal angewendet. Seeding legt fehlende Menüs an und lässt vorhandene unverändert; zweimal hintereinander ausgeführt.
  • Migration 20260912000400 zuerst gegen eine Kopie der lokalen Datenbank geprüft und die erzeugten Anweisungen kontrolliert: vier reine ALTER TABLE … ADD COLUMN, kein Tabellenneuaufbau. Danach nach konsistentem Backup unter var/backups/before-hierarchy-20260912-102402.db lokal angewendet; Backfill, Indexumstellung und unveränderte Dokumente überprüft. Wiederholtes Seeding und Produktionsbuild erfolgreich.
  • Composer-/Format-/Twig-/YAML-/Containerprüfungen, Migration, wiederholtes Seeding und Produktionsbuild erfolgreich.
  • Frische Quellkopie ohne bestehende DB/Secrets/Dev-Abhängigkeiten erfolgreich eingerichtet; gespeicherte Seite als HTTP 200 gerendert.
  • Browserprüfung aller drei öffentlichen Seiten mobil und Desktop; keine axe-Verstöße, keine Überläufe, funktionierende Sprunglink-/Navigationsabläufe auch ohne JavaScript.
  • Editor-End-to-End in isolierter Installation: Anmeldung, Auswahl im Iframe, Texteingabe, Undo/Redo, Einfügen/Duplizieren/Verschieben/Drag-and-drop/Löschen, identisches Neuladen, private Entwürfe, Veröffentlichung, Dialog-Fokus/Escape, simulierter Netzwerkfehler mit Wiederholung, 409-Konflikt mit JSON-Export und Server-Neuladen. Einschränkungen der axe-Frameprüfung sind in QUALITY dokumentiert.
  • Ausbau im Browser geprüft: Struktursuche und Auswahl per Tastatur, Einklappen, Abstände, Schriftwerte und Vererbung in echten Gerätebreiten, Rücksetzen/Undo, responsive Zweispaltenvorlage und identische Styles nach Speichern/Publizieren auf allen drei Geräten.
  • Editoroberfläche, Login und Seitenübersicht ohne axe-Verstöße; identisch gerenderter bearbeiteter öffentlicher Inhalt separat geprüft. Desktop- und Mobil-Screenshots visuell durchgesehen.
  • Lighthouse-Startseite: jeweils drei echte mobile und Desktop-Läufe, Median in allen fünf Kategorien 100. Nur lokaler Prototyp; keine Kundenwebsite-Abnahme. Umfang und Grenzen siehe QUALITY.
  • Gestaltete v2-Testseite mit Design-CSS und responsiver Abschnittsvorlage: zusätzliche sechs Lighthouse-Läufe, alle fünf Kategorien jeweils 100; LCP-Median mobil 901 ms/Desktop 240 ms, CLS/TBT 0. Echte Seitenbestände und geöffnete Browserentwürfe unangetastet. Bericht: docs/quality/editor-design-lighthouse.json.
  • v3-Galerieseite mit echten optimierten Testbildern und Effekten: sechs Lighthouse-Läufe, alle fünf Kategorien jeweils 100; LCP-Median mobil 937 ms/Desktop 279 ms, CLS/TBT 0. Upload, Metadaten, Mehrfachauswahl per Tastatur, Bildbeschriftung, Sortier-Undo, Archivieren/Wiederherstellen, Bewegungsreduktion und öffentliche Bilder im isolierten Browserablauf bestanden. Keine axe-Verstöße in Desktop-/Mobil-Mediathek; Screenshots geprüft. Berichte: docs/quality/editor-media-browser.json und editor-media-lighthouse.json.
  • Keine bekannten Dependency-Advisories im ausgeführten Composer-Audit/npm-Installationsaudit.

Gemessen am Nachbau selbst (13.09.2026, Fassung 1.4.3): je drei mobile und drei Desktop-Läufe auf /, /projekte und /impressum im Produktionsmodus, Median. Barrierefreiheit, Best Practices, SEO und agentisches Browsen überall 100. Leistung: /projekte und /impressum 100/100, die Startseite 99 mobil / 97 Desktop – dort bleibt das blockierende Stylesheet (rund 300 ms erste Anzeige) das Letzte, und das ist zu einem guten Teil der Entwicklungsserver ohne Kompression und Cache-Kopfzeilen. Bericht: docs/quality/kantit-lighthouse.json. Auf dem echten Hosting nachmessen – dort greifen die Regeln aus public/.htaccess.

Bekannte Einschränkungen

  • Gestaltung über geprüfte Tokens, keine beliebigen Pixelwerte, eigenen Schriften oder freien Farben. Noch keine Inline-Textbearbeitung, Formulare, dynamischen Menüs, Plugin-/Themeverwaltung oder WordPress-Import. Abschnittsvorlagen sind lokale Blockrezepte, keine globale Templateverwaltung.
  • Mediathek derzeit für statische Bilder: keine Videos/PDFs/SVGs, Lightbox/Slideshow, freie Bildzuschnitte, Ordner, Paginierung oder physische Löschung. Bild-URLs sind öffentlich; Archivieren schützt keine vertraulichen Dateien. Originale werden durch optimierte Varianten ersetzt. Einblendungen starten beim Laden, nicht beim Scrollen.
  • Sektionen bieten die vorhandenen Farb-/Breitentokens geräteübergreifend. Benutzerverwaltung und Passwortänderung sind noch nicht als Browseroberfläche verfügbar.
  • Der Aufgabenplan ist von Anfragen abhängig. Ohne Besucher und ohne geöffnetes Backend läuft nichts; minutengenau ist nur ein echter Cronjob oder der URL-Cronjob des Hosters. Unter Apache mit mod_php belegt eine laufende Aufgabe einen PHP-Prozess, nachdem die Seite beim Besucher ist (dort gibt es kein fastcgi_finish_request()) – deshalb sind die Grenzen je Seitenaufruf klein (2 Aufgaben, 20 Sekunden) und der Auslöser abschaltbar. Die Sicherung ist die schwerste Aufgabe und läuft trotzdem mit: eine tägliche Sicherung ist mehr wert als keine. Nicht geprüft: das Verhalten unter mod_php und unter echter Last – hier lief alles gegen den eingebauten PHP-Server.
  • Offene redaktionelle Feststellung: /grundlagen trägt einen im Editor gesetzten Textblock in #d08080 auf hellem Grund – 2,75:1, unter der AA-Schwelle. Die Farbe wurde nicht verändert, weil sie eine Nutzerentscheidung ist; die Prüfung nennt sie bei jedem Lauf, und die Kontrastwarnung im Editor meldet sie ebenfalls.
  • Aus Umuts erweitertem Auftrag noch offen (Paket G2 im Ausbauplan): die Neugestaltung der Verwaltung.
  • Eigenes CSS kennt keine Gerätevarianten – dafür sind die Vorschaugrößen im Designbereich da. Eigenes Skript hat keine Fassungsverwaltung außer der Seitenhistorie und keine Prüfung auf Syntaxfehler; ein fehlerhaftes Skript schreibt in die Browserkonsole und lässt die Seite sonst unangetastet.
  • Formatierter Text gilt nur für Textblöcke. Überschrift, Button, Zitat und die Titel von Karten, Tabs und Akkordeon bleiben Klartext; Auszeichnung in einer Überschrift bringt mehr Ärger als Nutzen. Die Werkzeugleiste ist nicht mit einem Screenreader geprüft.
  • Offen: der Papierkorb wird nicht automatisch geleert, es gibt keine Aufbewahrungsfrist, und Weiterleitungen aus einem Verschiebevorgang lassen sich nur einzeln in den Einstellungen entfernen.
  • Weiterhin keine Erprobung mit einem Screenreader. Bei der Direktbearbeitung wiegt das: dass der Fokus sinnvoll wandert und der Zustandswechsel angekündigt wird, ist durch axe nicht abgedeckt.
  • Gestaltungsvorlagen lassen sich in der Oberfläche nur anlegen und anwenden, nicht umbenennen; das Entfernen geht bisher nur über die Schnittstelle. Es gibt keine Vorschau einer Vorlage.
  • SEO-Titel und -Beschreibung sind weiterhin die Dokumentfelder; getrennte SEO-Titel gibt es nicht. Das Vorschaubild wird über seine Mediathek-ID eingetragen und noch nicht als og:image aufgelöst. Weiterleitungen werden nur bei genauer Übereinstimmung des Pfades gefunden, ohne Muster und ohne Berücksichtigung von Abfrageparametern; Seiten umbenennen erzeugt noch keine automatische Weiterleitung (Paket F).
  • Dateiuploads in Formularen fehlen – Anhänge brauchen einen nicht öffentlichen Ablageort mit Zugriffsschutz und eine eigene Typprüfung; Begründung in ADR 017. Ebenfalls offen: Formulare lassen sich nicht löschen oder umbenennen, Einsendungen nicht exportieren oder löschen, und es gibt keine Aufbewahrungsfrist. Der tatsächliche E-Mail-Versand ist nie ausgeführt worden: die Tests nutzen einen Transport, der nichts sendet.
  • Das Tab-Skript ist nicht im Browser geprüft: Pfeiltastenbedienung, aria-selected, das Verstecken der Panels und die Auswahl über ein Sprungziel sind nur im Code umgesetzt, nicht ausgeführt. Geprüft ist bisher ausschließlich die serverseitig erzeugte Auszeichnung und die Sicherheitsrichtlinie.
  • Die neuen Blöcke lassen sich nicht in einer Vorlage bündeln: die drei Abschnittsvorlagen sind unverändert. Die Logoleiste hat keine Graustufen- oder Größenoption, die Kundenstimme kein Bild, die Symbolkarte keine eigene Farbe.
  • Beiträge haben noch keine Vorschau des Beitragsbilds in der Verwaltung: das Bild wird über seine Mediathek-ID eingetragen. Es gibt keine Begriffsverwaltung (Umbenennen, Zusammenführen, Löschen ungenutzter Begriffe), keine Beitragssuche und keine Archivbeschreibungen. Die Paginierung der Übersicht ist auf zehn Beiträge je Seite festgelegt.
  • Menüs kennen nur die beiden festen Plätze Hauptnavigation und Fußzeile; weitere Plätze, eigene Menüs pro Seite und automatisch aus der Hierarchie erzeugte Einträge gibt es nicht. Die Einträge werden über Position und Ebene sortiert, nicht per Drag-and-drop.
  • Seiten lassen sich anlegen und löschen, aber noch nicht umbenennen oder verschieben: eine Adressänderung müsste die Pfade aller Unterseiten mitziehen und Weiterleitungen anlegen. Beiträge werden gespeichert und ausgegeben, haben aber noch keine Übersichtsseite, keine Sortierung nach Datum und keine strukturierten Daten. Die Hauptnavigation ist weiterhin fest im Template; verschachtelte Seiten erscheinen dort nicht automatisch.
  • Für diesen Schritt wurden bewusst keine Browser-, Lighthouse- oder axe-Läufe ausgeführt (Nutzerentscheidung). Oberfläche und Ausgabe der neuen Formulare sind nur über die Funktionstests geprüft, nicht visuell.
  • SQLite tatsächlich getestet. MySQL-/MariaDB-Kompatibilität und CI erst vorbereitet, nicht nachgewiesen; Anbieter-/Tarifdetails und Livebetrieb offen.
  • Der Erhaltungsvertrag für Plugin-Blöcke steht, ein Pluginsystem nicht: Registrierung, Hooks, Abhängigkeiten und Ressourcenladen folgen in Phase 4. Kinder eines Plugin-Blocks werden gespeichert, aber nicht gerendert. Kern-Designtokens gelten für sie nicht.
  • Kein Inhaltscache, nur Symfony-Programmcache. Seiten senden no-store; Veröffentlichung ist dadurch ohne Cacheinvalidierungsverzögerung sichtbar.
  • Noch keine repräsentativen Kundeninhalte/Bilder/Blog-/Formularseite, keine Felddaten und keine vollständige manuelle Barrierefreiheitsprüfung.
  • Die vor der Hostingantwort erzeugte .venv bleibt ungenutzt und von Git ausgeschlossen. Die automatische Freigabeprüfung blockierte ihren Löschversuch mit „blocked by policy“; keine Umgehung versucht. Auch var/fresh-check, DB, Secrets und Berichte sind lokal ignoriert.

Arbeitspaket H, Teil 1: die Vorschau wird zur Arbeitsfläche

Erledigt. Der Abgleich mit dem Flatsome UX Builder steht in docs/FLATSOME-ABGLEICH.md: die Gestaltung ist Flatsome bereits voraus (93 freie Eigenschaften je Gerät und für Hover gegen feste Optionslisten), die Lücken liegen beim Elementkatalog (20 offene Elemente ohne Shop) und bei der Bedienung. Umuts Entscheidung: Bedienung zuerst, Shop-Elemente ausgelassen.

Umgesetzt ist die erste Hälfte der Bedienung:

  • Werkzeugleiste am Element in der Vorschau: Name des Elements, Elternelement auswählen, nach oben, nach unten, duplizieren, löschen. Sie trägt den Namen des Elements („Sektion duplizieren“) statt des nackten Verbs – die Seitenleiste hat dieselben fünf Aktionen, und zwei gleichnamige Bedienelemente auf einem Bildschirm sind für eine Sprachausgabe nicht unterscheidbar. Beide Wege rufen dieselben Funktionen auf.
  • Auswahl- und Überfahrrahmen mit Beschriftung direkt in der Vorschau.
  • Einfügepunkte: ein Pluszeichen an der Ober- und Unterkante des gewählten Elements setzt eine Marke; das nächste Element aus dem Werkzeugkasten landet genau dort statt am Ende.
  • Ziehen aus dem Werkzeugkasten in die Vorschau, mit Ablegemarke zwischen den Elementen und Rahmen um einen Behälter, der das Element aufnimmt. Der Strukturbaum zieht in dieselbe Fläche.
  • Im Modell dafür neu: insertBlockAt, moveBlockTo und dropList – punktgenaues Einfügen und Verschieben statt „hinter das Gewählte“.

Die Ebene liegt im Editordokument über dem Rahmen, nicht darin: die Vorschau wird bei jeder Änderung vollständig ersetzt, und eingezeichnete Bedienelemente wären damit weg und stünden außerdem in der Auszeichnung, die die Vorschau wahrheitsgetreu zeigen soll. Sie trägt denselben Skalierungsfaktor wie der Rahmen; Leiste und Pluszeichen werden gegenskaliert und bleiben in der Tablet- und Mobilvorschau fingergroß.

Geprüft. npm test 22 von 22 (vier neue Tests für Einfügen, Verschieben, Behälterregeln und die oberste Ebene). php vendor/bin/phpunit 434 von 434. npm run test:editor vollständig, mit einem neuen Abschnitt für Werkzeugleiste, Einfügepunkt und Ziehen; alle axe-Prüfungen ohne Verstoß, auch die mobile Editoransicht. Screenshot unter reports/editor/canvas-overlay.png.

Dabei gefunden und behoben: die ganze Ebene war zunächst aria-hidden und enthielt fokussierbare Schaltflächen – axe hat das gemeldet. canContain las block.type an einem fehlenden Block und zerbrach beim Ziehen aus dem Werkzeugkasten, wo der Block noch nicht existiert. Beim Verschieben ans Listenende wurde der Index begrenzt, bevor die Entnahme verrechnet war; der Block landete eine Stelle zu früh.

Bekannte Grenzen. Die Vorschau wird weiterhin nach 250 ms serverseitig neu erzeugt – das Ablegen fühlt sich dadurch nicht so unmittelbar an wie bei Flatsome, die Wahrheit bleibt aber der Server. Die Werkzeugleiste ist nicht mit einem Screenreader erprobt, nur mit axe. Spalten lassen sich noch nicht in der Vorschau breiter ziehen, der Werkzeugkasten ist weiterhin eine Textliste ohne Vorschaubilder, und es gibt je Option noch keinen eigenen Geräteschalter.

Arbeitspaket H, Teil 2: der Werkzeugkasten kennt den Zusammenhang

Erledigt. Umuts Einwand: Zeile und Spalte standen oben in der Auswahl, als wären sie Seitenanfänge. Sie gehören in den Inhalt – erst eine Sektion, dann eine Zeile darin. Das war nicht nur eine Anzeigefrage: eine Zeile nahm bisher jedes Kind an, eine Überschrift konnte neben den Spalten landen, und eine Spalte durfte überall stehen. Die Struktur, die der Editor in seinem eigenen Hinweistext beschreibt – Sektion → Zeile → Spalte → Inhalt –, galt für die Schaltflächen nicht.

Übernommen ist Flatsomes Regelmodell, weil es die Fälle abdeckt und weil es das ist, was Umut in den Händen hat (inc/builder/core/server/src/Collections/Elements.php):

Regel Flatsome KantSites jetzt
allow row => ['col'], accordion => ['accordion-item'] ALLOW – Zeile nimmt nur Spalten
require col => ['row'], tab => ['tabgroup'] REQUIRE – Spalte lebt nur in einer Zeile
— (Flatsome kennt das nicht) ROOT_NEEDS_CONTAINER – eine Zeile ist kein Seitenanfang

Der dritte Punkt ist der Zusatz: Flatsome lässt eine Zeile auch lose am Dokumentanfang zu. Genau das ist das „aus dem Nichts“, das Umut stört, also wird es unterbunden – in jedem Behälter dagegen bleibt die Zeile erlaubt, auch in einem Tab oder einem Akkordeon-Eintrag.

Daraus folgt die Bedienung:

  • Der Werkzeugkasten ist nach Aufbau gruppiert – Aufbau, Inhalt, Bausteine, Gruppen, Seite und Daten – statt einer flachen Liste aus 31 Typen.
  • Was an der gewählten Stelle nicht hingehört, ist gesperrt und sagt beim Überfahren warum („Spalte: gehört in Zeile. Wähle dort zuerst ein Element aus.“). Eine Regel, die man erklärt bekommt, lernt man; eine Schaltfläche, die nur nicht mehr reagiert, drückt man weiter.
  • Was möglich ist, sagt wohin es kommt („Wird in Zeile eingefügt.“).
  • Die Zeile wird nach Spaltenzahl angeboten (2, 3, 4) und bringt ihre Spalten mit. Der separate Zeilenbereich über der Liste ist entfallen.
  • Eine Regel für alle Wege: insertionTarget beantwortet „wohin käme das?“, ohne etwas einzufügen. Knopf, Einfügepunkt, Ziehen und die Sperrlogik des Werkzeugkastens nutzen dieselbe Funktion – vorher entschied insertBlock allein, niemand konnte vorher fragen, und insertRow hatte eine eigene zweite Kopie derselben Platzierungslogik.

Ein Element, das der gewählte Behälter nicht nimmt, wird nicht gesperrt, sondern steigt auf, bis ein Elternteil es annimmt: eine Überschrift bei gewählter Zeile landet hinter der Zeile in der Sektion. Die Absicht ist eindeutig, nur die Zeile ist der falsche Ort.

Nebenbei behoben: postnav fehlte seit Schema v18 in labels und icons – der Strukturbaum zeigte für die Beitragsnavigation „Unbekannt“ und ein Fragezeichen.

Geprüft. npm test 27 von 27 (fünf neue: Zeilen-/Spaltenregel, oberste Ebene, Übereinstimmung von Auskunft und Tat, Kategorie-Vollständigkeit, Zeile mit Spalten an fester Stelle). php vendor/bin/phpunit 446 von 446. npm run test:editor vollständig mit neuem Abschnitt „the toolbox follows the structure“; alle axe-Prüfungen ohne Verstoß. Screenshot unter reports/editor/toolbox.png.

Bekannte Grenzen. Inhalt darf weiterhin lose am Dokumentanfang stehen – der Validator erlaubt es, und eine weitere Einschränkung wäre erfunden statt übernommen. Bestehende Dokumente werden nicht nachträglich geprüft: eine Überschrift, die früher direkt in einer Zeile gelandet ist, wird weiterhin ausgegeben und lässt sich auswählen und löschen, nur nicht mehr dorthin verschieben. Der Werkzeugkasten hat weiterhin keine Vorschaubilder und keine Suche.

Arbeitspaket H, Teil 3a: die Optionen selbst

Erledigt. Drei Bedienungen, die Flatsome an jeder Option hat und die hier fehlten.

Geräteschalter je Option. Bisher entschied ein einziger Schalter über der Vorschau, welches Gerät das ganze Bedienfeld bearbeitet. „Ist die Überschrift auf dem Telefon kleiner?“ ließ sich nur durch Umschalten und Hinsehen beantworten. Jetzt trägt jede Option drei Chips (D, T, M): sie zeigen, wo ein eigener Wert liegt, und ein Klick wechselt dorthin. Das ist mehr als Flatsome bietet – dort schaltet man um, hier sieht man vorher, ob es überhaupt etwas zu sehen gibt. Bedienfeld und Vorschau wechseln gemeinsam: einen Wert für ein Gerät zu setzen, das man nicht ansieht, ist blindes Ändern.

Abstand als Rahmen. Innenabstand, Außenabstand, Rahmenstärke und Ecken standen als vier Felder untereinander, beschriftet „Oben, Rechts, Unten, Links“ – man musste lesen, um zu wissen, welche Kante man setzt. Jetzt sitzt jedes Feld an seiner Kante, die Lage ist die Beschriftung. In der Mitte verkettet ein Schalter alle vier. Er startet aus: er ersetzt den früheren Knopf „Alle Seiten gleich setzen“, und das war eine bewusste Handlung. Die Einheit bleibt eine Wahl für die ganze Eigenschaft – vier Einheitenmenüs passen nicht in ein 240 Pixel breites Feld. Seiten mit unterschiedlichen Einheiten werden als „gemischt“ geführt und bleiben unangetastet, bis eine Einheit tatsächlich gewählt wird; ein gespeichertes 2rem wird nicht still zu 2px.

Ziehfelder. Zahlenfelder lassen sich seitwärts ziehen. „24“ tippen, schauen, „26“ tippen, schauen, „22“ tippen ist drei Anläufe für eine Entscheidung; Ziehen beantwortet sie in einer Bewegung. Umschalt grob, Alt fein. Tippen und Pfeiltasten bleiben unverändert. Nur für Maus und Stift: auf einem Tippgerät ist ein seitliches Ziehen über ein Feld die Art, wie das Bedienfeld gescrollt wird.

Dabei gefunden und behoben: Der Zug war zunächst nach einem Pixel zu Ende – jede Änderung zeichnete das Bedienfeld neu und ersetzte damit genau das Feld, an dem der Zeiger hing. Das Bedienfeld bleibt während eines Zuges stehen und wird einmal neu gezeichnet, wenn losgelassen wird (keepPanel). Der Kettenschalter verlor aus demselben Grund seinen Zustand, sobald er gedrückt wurde; er liegt jetzt außerhalb der Darstellung. Und das Gleichziehen nahm den Wert des ersten Feldes, auch wenn es leer war – ein Druck auf die Kette löschte damit die Seiten, die einen Wert hatten. Es nimmt jetzt den ersten vorhandenen Wert.

Geprüft. npm test 27 von 27. php vendor/bin/phpunit 452 von 452. npm run test:editor vollständig mit neuem Abschnitt „device chips, spacing box and scrub fields“: Chips zeigen den richtigen Zustand und wechseln Bedienfeld und Vorschau zusammen, der Rahmen ändert ohne Kette nur eine Seite und mit Kette alle vier, die Einheitenumstellung erhält die Zahlen, und ein Zug über 20 Pixel hebt 72 auf 92 und ist ein Undo-Schritt. Alle axe-Prüfungen ohne Verstoß. Screenshot unter reports/editor/options.png.

Bekannte Grenzen. Die Chips zeigen einen eigenen Wert, aber nicht, welchen – dafür muss man weiterhin hinwechseln. Auf Tippgeräten gibt es kein Ziehen. Der Rahmen zeigt geerbte Werte als Platzhaltertext, nicht als eigene Kennzeichnung. Noch offen aus Teil 3: Vorschaubilder und Suche im Werkzeugkasten, Vorlagen je Element mit Bild, und das Ziehen der Spaltenbreite in der Vorschau.

1.5.3: der Editor blieb nach dem Update schwarz

Der Fehler. Nach 1.5.2 meldete der Editor im Browser The requested module './editor-model.js' does not provide an export named 'dropList', und der Werkzeugkasten war leer.

Ursache war nicht der Code, sondern das Zwischenspeichern. public/.htaccess setzt für jede .js-Datei Cache-Control: public, max-age=31536000, immutable – ein Jahr. Die Fassungskennung ?v= hängt aber nur an den Einstiegsdateien, die die Vorlage schreibt. Die fünf Module, die der Editor über import './…​.js' lädt, tragen keine: der Browser holte das neue editor.js und behielt das alte editor-model.js. Ein neues canvas-overlay.js traf damit auf ein Modell ohne dropList.

Beide gemeldeten Symptome hatten diese eine Wurzel: seit 1.5.1 baut das Skript den Werkzeugkasten auf (buildLibrary()), und editor.js importiert canvas-overlay.js in Zeile 5. Bricht der Import, läuft keine Zeile danach – und die Elementliste bleibt leer. Das gemeldete „Menü fehlt“ war der Werkzeugkasten, nicht die Verwaltungsnavigation. Die wurde geprüft: in Chrome 152 stehen ::details-content, ausgeblendeter Schalter und sichtbare Liste korrekt.

Die Behebung. Eine Importkarte in base.html.twig hängt die Fassung an die aufgelöste Adresse jedes Moduls, ohne dass eine einzige Importzeile sie kennen muss. Das behält das Zwischenspeichern über ein Jahr – die Leistung war nie das Problem, nur die fehlende Kennung.

Eine Importkarte muss eingebettet sein; die Spezifikation kennt keine externe. Unter script-src 'self' wurde sie wortlos verworfen. AdminHeadersSubscriber erzeugt deshalb je Verwaltungsseite einen Nonce, nennt ihn in der Richtlinie und legt ihn in die Anfrage; die Twig-Funktion csp_nonce() gibt ihn der Karte mit. Der Nonce erlaubt genau dieses eine Skript – 'self' gilt unverändert für alles andere.

Geprüft. Der Browsertest schreibt jede angeforderte .js-Adresse mit und verlangt an jeder eine Fassungskennung; vor der Behebung nennt er die fünf fehlenden beim Namen, danach trägt sie alle acht. Dazu ein Test, der jeden ./…​.js-Import in public/assets gegen die Importkarte hält – ein neues Modul, das dort fehlt, fällt damit im Test auf und nicht erst beim nächsten Update beim Kunden. Gegenprobe gemacht: mit einem entfernten Eintrag schlägt er fehl. npm test 28 von 28, php vendor/bin/phpunit 452 von 452, npm run test:editor vollständig.

Bekannte Grenze. Ein Modulfehler legt den Editor weiterhin vollständig lahm, und der Werkzeugkasten ist seit 1.5.1 nichts ohne sein Skript. Für einen Editor ist das vertretbar – die öffentliche Website läuft ohne JavaScript –, aber die Fehleranzeige dafür ist noch die Browserkonsole und kein Satz in der Oberfläche.

Arbeitspaket H, Teil 3b: der Werkzeugkasten bekommt Bilder und eine Suche

Erledigt. Die Elementwahl war eine Liste aus Schriftzeichen – „▣“ für Sektion, „☰“ für Zeile. Jetzt trägt jeder der 34 Einträge eine Strichzeichnung, die zeigt, was er baut: die Sektion ein gefülltes Band, „Zeile, 3 Spalten“ tatsächlich drei Kästen, das Bild einen Rahmen mit Bergzug.

Zeichnungen statt Bilddateien. Flatsome liefert je Element ein PNG mit. Gezeichnet ist hier besser: library-previews.js ist ein paar hundert Byte statt eines Ordners voller Bitmaps, die Formen nehmen die Farbe der Fläche an, auf der sie liegen – gesperrte Einträge werden damit von selbst blass – und sie bleiben auf jedem Bildschirm scharf. Die Zeilenvorschau wird gerechnet, nicht gezeichnet: zwei, drei oder vier Kästen aus derselben Funktion.

Suche. Ein Feld über der Liste filtert über alle Gruppen. Gesucht wird über den Namen, den internen Typ und den Gruppennamen – wer „slider“ eingibt, muss nicht wissen, dass er unter „Gruppen“ einsortiert ist. Gruppen ohne Treffer verschwinden, damit die Liste keine Lücken behält, und darunter steht, wie viele von wie vielen übrig sind.

Dabei bemerkt: Es gab damit zwei Suchfelder mit dem Namen „Element suchen“ auf einem Bildschirm – für eine Sprachausgabe nicht unterscheidbar. Sie heißen jetzt nach dem, was sie durchsuchen: „Element zum Einfügen suchen“ im Werkzeugkasten, „Element auf der Seite suchen“ im Strukturbaum.

Geprüft. Der Browsertest zählt die Vorschaubilder gegen die Einträge (alle 34 haben eines), sucht nach „galerie“ (ein Eintrag, eine Gruppe), nach „aufbau“ (fünf Einträge über den Gruppennamen), nach etwas Nichtvorhandenem und wieder nach nichts. npm test 28 von 28, php vendor/bin/phpunit 452 von 452, npm run test:editor vollständig ohne axe-Verstoß.

Noch offen aus Teil 3. Vorlagen je Element mit Vorschaubild. Und das Ziehen der Spaltenbreite: das geht heute nicht ohne einen Zwischenschritt, denn eine Zeile ist ein CSS-Raster mit gleich breiten Spalten (repeat(n, minmax(0,1fr))); ein span je Spalte gibt es nicht. Dafür braucht es erst ein Zwölf-Spalten-Modell im Designtoken-Manifest, und das berührt die öffentliche CSS und damit die Messwerte aus AGENTS.md. Das ist ein eigener Schritt, kein Nebenbei.

1.5.5: die Verwaltungsnavigation war am Schreibtisch unerreichbar

Der Fehler. In der ausgelieferten Fassung fehlte in der Verwaltung am Schreibtisch die gesamte linke Navigation – Marke oben, Fußzeile unten, dazwischen nichts. Kein Weg zu Seiten, Mediathek, Einstellungen außer über die Adresszeile.

Die Navigation steckt in einem <details>: auf dem Telefon ein Klappmenü, am Schreibtisch die stehende Spalte. Am Schreibtisch wurde der Schalter mit .shell-menu > summary { display: none } ausgeblendet – aber das Element trug kein open, und ein geschlossenes details versteckt seinen Inhalt selbst. Schalter weg und Inhalt weg.

Die Behebung. ::details-content holt den Inhalt am Schreibtisch zurück, während der Schalter ausgeblendet bleibt. Kennt ein Browser die Regel nicht, fällt über @supports die ganze Gruppe weg: dann steht der Schalter da und die Liste ist einen Klick entfernt – statt gar nicht da. Auf dem Telefon bleibt es beim Klappmenü.

Der Code dafür lag bereits im Arbeitsverzeichnis: eine parallel laufende Sitzung hatte ihn geschrieben, aber nicht committet, und damit war er in keinem Release. Übernommen ist hier nur diese Korrektur, nicht die übrige Arbeit jener Sitzung.

Wie das durchrutschen konnte – und was dagegen getan ist. Beim vorigen Bericht wurde die Navigation für in Ordnung erklärt, nachdem eine Nachbildung der Regel in einer leeren Seite geprüft worden war. Die Nachbildung enthielt die Korrektur, die ausgelieferte Datei nicht; der Befund war damit wertlos. Jetzt prüft der Browsertest die echte Verwaltungsseite: am Schreibtisch kein Schalter, Navigation sichtbar, der Eintrag „Seiten“ mit messbarer Fläche; auf 390 px Breite ein Schalter, Liste zunächst zu, ein Klick öffnet sie. Gegenprobe gemacht – gegen die ausgelieferte admin.css schlägt der Test fehl.

Geprüft. npm run test:editor vollständig, npm test 28 von 28, php vendor/bin/phpunit 452 von 452.

1.6.0: der Aufgabenplan wird veröffentlicht

Diese Fassung bringt den Aufgabenplan ohne Cronjob (ADR 073, oben beschrieben) in ein Release. Er entstand in einer parallel laufenden Sitzung und lag bis hierher unveröffentlicht im Arbeitsverzeichnis – die Releases 1.5.0 bis 1.5.5 wurden bewusst aus dem jeweiligen Tag gebaut und enthielten ihn deshalb nicht.

Vor der Veröffentlichung geprüft, nicht bloß eingepackt:

  • php vendor/bin/phpunit 452 von 452, npm test 28 von 28, npm run test:editor vollständig.
  • Die Migration 20260914000100 legt nur die Tabelle scheduled_task an und nimmt sie beim Zurückrollen wieder weg. Sie fasst keine bestehenden Daten an – das ist die risikoarme Sorte.
  • Seit einer Stunde keine Schreibzugriffe der anderen Sitzung mehr; der Stand ist ruhig.

Dabei zwei Fehler behoben:

  • config/services.yaml verwendet !tagged_iterator. Das ist gültiges Symfony, aber php bin/console lint:yaml config kennt eigene Tags nur mit --parse-tags und gab 1 zurück – der CI-Lauf in .github/workflows/ci.yml wäre daran gescheitert. Das Flag ist ergänzt.
  • src/EventSubscriber/AdminHeadersSubscriber.php (aus 1.5.3) entsprach nicht dem Stil von php-cs-fixer; composer lint gab 8 zurück. Behoben, jetzt 0 von 128 Dateien zu ändern.

Nächster konkreter Schritt

Arbeitspaket H, Teil 3b: Vorschaubilder und Suche im Werkzeugkasten, Vorlagen je Element mit Vorschaubild und das Ziehen der Spaltenbreite in der Vorschau. Danach Arbeitspaket I: banner, textbox, stack, imagebox, logo, messagebox. Reihenfolge und Begründung in docs/FLATSOME-ABGLEICH.md.

Unverändert offen: den Editor mit einem Screenreader prüfen und die übrigen Verwaltungsbildschirme in die automatische Barrierefreiheitsprüfung aufnehmen. Danach der Rest von Arbeitspaket G (Inline-Ausbau, globale Bereiche für Header und Footer, wiederverwendbare Sektionen). Danach Arbeitspaket F aus docs/AUSBAUPLAN.md: Seiten umbenennen und verschieben mit Aktualisierung aller Kindpfade und automatischer Weiterleitung, frei wählbare Startseite, geplantes Veröffentlichen, Revisionsliste mit Wiederherstellung und ein Papierkorb statt sofortigem Löschen.

kant-it.de ist seit 12.09.2026 als Pilotwebsite bestimmt. Der öffentliche Bestand wurde erhoben (WordPress 7.1, Flatsome 3.20.9 mit eigenem Child-Theme, WPForms Lite, Complianz, Google-Rezensionen, eigenes Plugin kant-nova-chat) und die noch offene Arbeit daraus in docs/AUSBAUPLAN.md in elf Arbeitspakete aufgeschlüsselt. Erhoben wurde ausschließlich die öffentlich ausgelieferte Startseite; ohne Backendzugang sind Beitragsbestand, Formularkonfiguration, Benutzer und Flatsome-Shortcodes nicht inventarisierbar. Keine externe Veröffentlichung erfolgt.

Lokale Vorschau

Öffentlich: http://127.0.0.1:8080/. Verwaltung: http://127.0.0.1:8080/admin. PHP 8.4.22 im Symfony-Produktionsmodus; der Browser wurde als umut angemeldet. Der Prozess ist sitzungsabhängig; zum erneuten Start README verwenden.