0d6c1a17afa3a6cfc53703d2a65cf2a0aaab1ccd
21
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
0d6c1a17af |
Vier Darstellungen fuer die Beitragsliste
Karten (Raster mit Bild oben), Liste (Bild links, Text rechts), Kompakt (nur Titel und Datum) und Wechselseitig (Bild mal links, mal rechts). Dieselben Daten, dasselbe Markup - nur eine Klasse wechselt. Die erste Fassung von "Wechselseitig" war ein umgedrehter Flexkasten und sah auch so aus. Jetzt sitzt das Bild in einer Karte mit Ecken, Schatten und festem Verhaeltnis, der Text hat eine Nummer aus einem CSS-Zaehler, eine begrenzte Zeilenlaenge und einen sichtbaren Verweis, und zwischen den Reihen liegt eine feine Linie. Zwei Details: die Karte ist das Listenelement, also zaehlt :nth-child auf ihr - die erste Fassung wechselte deshalb gar nicht. Und die Umkehrung laeuft ueber order statt row-reverse, damit auf dem Telefon sauber wieder Bild ueber Text steht. Geprueft: 399 Tests unter PHP 8.2 und 8.4, 18 JS-Tests, Editortest, Browserpruefung, axe ohne Verstoss auf allen vier Darstellungen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
1b22265a54 |
Beitragsnavigation als Baustein (Dokumentschema v18)
Umut: "Beitragsseite gefaellt mir nicht, sollte wie bei kant-it.de sein oder besser. Falls noetig Plugin bauen." Die Originalseite hat Galerie, Vor/Zurueck zwischen Projekten und einen Abschlussaufruf. Galerie und Aufruf gab es laengst - sie waren nur nicht benutzt. Die Navigation gab es nicht. Kein Plugin: ein Plugin ist hier ein Bausteintyp, dessen Eigenschaften der Kern aufbewahrt, aber nicht rendert - es koennte also nichts ausgeben, solange es keine Laufzeit dafuer gibt. Und die Nachbarn eines Beitrags stehen in derselben Tabelle wie die Beitragsliste, die der Kern schon fuellt. Also ein Baustein: postnav. Gespeichert sind zwei Beschriftungen, mehr nicht - welcher Beitrag vorher und nachher kommt, entscheidet der Renderer. Gesucht wird innerhalb derselben Kategorie, und am Rand wird nicht umgebrochen. Ausserdem: die uebernommenen Projekttexte begannen mit dem wiederholten Titel (20 Beitraege), und schmale Abschnitte standen links statt mittig - max-width ohne margin:auto ist eine halbe Einstellung. Geprueft: 396 Tests unter PHP 8.2 und 8.4, 18 JS-Tests, Editortest, Browserpruefung, axe ohne Verstoss auf der Beitragsseite. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
5a5933f8cd |
Slider mit Autorotation, Beitragsliste mit Blaettern
Zwei Wuensche, zwei Bausteine. Die Autorotation ist Sekunden, kein Haekchen: 0 heisst aus, ab 3 laeuft sie. Sie braucht ein Skript - das zweite auf einer oeffentlichen Seite nach den Tabs - und es kommt nur auf Seiten mit einem solchen Slider, sonst bleibt script-src 'none'. Es laeuft nicht bei "weniger Bewegung", hoert beim ersten Tippen, Klicken, Wischen oder Scrollen endgueltig auf und pausiert bei Zeiger, Fokus, Hintergrundtab und ausserhalb des Bildes. Ohne Skript bleibt der Slider vollstaendig bedienbar. Die Beitragsliste kann blaettern statt abzuschneiden. Eine Auswahl auf der Startseite zeigt die neuesten und hoert auf; eine Uebersichtsseite blaettert und sagt, wie viele es sind. Das war die eigene Regel aus Arbeitspaket M, die fuer diesen Baustein noch nicht galt. Beim Bauen zweimal dieselbe Falle wie vorgestern: eine Variable war in der Schliessung nicht gebunden. Einmal das Sprungziel des Sliders (-78 statt 666), einmal die Seitenzahl - die zweite fiel sofort auf, weil die Seite mit 500 antwortete. Geprueft: 395 Tests unter PHP 8.2 und 8.4, 18 JS-Tests, Editortest, Browserpruefung, axe ohne Verstoss. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
e21dff91fe |
Slider mit Punkten, Karten als Karten, hervorgehobenes Wort
Umut hat den Nachbau Detail fuer Detail mit kant-it.de verglichen. Vier Stellen, an denen das CMS etwas nicht konnte oder etwas Falsches tat. Der Slider zeigte eine Rollleiste statt Punkte. Punkte zeichnet jetzt der Browser selbst - scroll-marker-group und ::scroll-marker, ohne Skript, mit erkennbarem aktivem Punkt. Wo ein Browser das noch nicht kennt, bleibt das wischbare Schiebefeld. Pfeile gibt es bewusst nicht: Chrome legt ::scroll-button an den Seitenrand statt in den Slider. Und er rotiert nicht von selbst, weil eine oeffentliche Seite kein JavaScript laedt. Eine Beitragskarte war keine Karte, sondern eine lose Spalte. Jetzt weiss, 18px Ecken, Bild buendig oben im Verhaeltnis 16:10, Text darunter. Die Theme-Schriftskala ueberschrieb die Groessen der Bausteine: jede Kartenueberschrift war so gross wie eine Abschnittsueberschrift. Eine Ueberschrift darf ab v17 ein Wort hervorheben. Dabei waere beinahe eine Luecke entstanden: die Dokumentfassung war in der Schliessung des Filters nicht gebunden, der Filter lief nie, die Ausgabe war schon unescaped. Ein Test mit <script> in einer Ueberschrift hat es gezeigt. Geprueft: 390 PHP-Tests unter 8.2 und 8.4, 18 JS-Tests, Editortest, Browserpruefung, axe ohne Verstoss auf dem Nachbau. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
70283db20a |
Dokumentschema v17: Projektraster, verlinktes Bild, klebender Kopfbereich
Alles aus dem Nachbau von kant-it.de, den Umut durchgesehen hat. Eine Beitragsliste kann Datum, Autor, Auszug und Kategorie ausblenden und sieht damit aus wie ein Projektraster statt wie ein Blogarchiv. Beinahe lautlos danebengegangen: |default(true) macht in Twig aus einem gespeicherten false ein true - die Einstellung haette sich speichern lassen und nichts getan. Der Test prueft beide Richtungen. Ein Bild darf verlinken, damit ein Logo auf die Startseite zeigt; die Adresse geht durch dieselbe Pruefung wie jeder andere Link. Und der Kopfbereich kann beim Scrollen oben bleiben - als Einstellung der Website, weil nur das umschliessende Element kleben kann. Der groesste Fund steckt im Editor: die Liste bekannter Schemafassungen endete bei 12, waehrend Bausteine v13 bis v15 verlangen. In einem aktuellen Dokument liess sich damit kein Baustein mehr einfuegen, und Symbol, Fortschritt, Slider und externer Inhalt in gar keinem. Jetzt eine Zahl an einer Stelle, mit einem Test ueber jeden Typ und jede Fassung. Geprueft: 389 PHP-Tests unter 8.2 und 8.4, 18 JS-Tests, Editortest, Browserpruefung. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
c3bff7d936 |
Bilder in vier Breiten, Releasearchiv fuer Hosting ohne Composer
Uploads werden weiterhin nach WebP umgewandelt, jetzt in vier statt drei Breiten (480/960/1600/2400) und ohne Hochskalieren. Hintergrundbilder waehlen ihre Variante je Geraet, weil CSS kein srcset kennt. Dabei kam ein Fehler heraus, den erst der Browsertest zeigte: der Dokumentpruefer erlaubte drei sources je Bild, also war mit der vierten Variante jedes Dokument mit einem Bild ungueltig - ohne sichtbare Meldung. Die Grenze haengt jetzt an MediaLibrary::WIDTHS. Geteiltes Hosting hat meistens keinen Composer. scripts/build-release.php baut deshalb auf einem Build-Rechner ein Archiv mit fertigem vendor/ ohne Entwicklungspakete, samt SHA-256. Der Updater bricht ab, wenn ein Update composer.lock aendert und Composer auf dem Server nicht aufrufbar ist, statt mit einem unpassenden vendor/ weiterzulaufen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
af3589c95c |
Seitenbreite wirkt, vier Menueplaetze, Seiten ohne Kopfbereich
Drei Fragen, drei unterschiedliche Antworten. Die globale Seitenbreite war einstellbar und wirkte nur in eine Richtung: site.css schrieb width:min(1180px,...), also schluckte die 1180 jeden groesseren Wert aus dem Designbereich. Schmaler ging, breiter nicht, ohne jede Meldung. Die Zahl kommt jetzt aus dem Theme. Das ist der dritte Fehler dieser Art an einem Tag - nach der Rahmenstaerke und der Bildrundung -, und alle drei hatten dieselbe Ursache: CSS, das nie gegen eine Erwartung geprueft wurde. Menueplaetze und Menueart waren vermischt. Wo ein Menue steht, bestimmt der Block und war immer frei. Welche Eintraege es zeigt, bestimmt der Platz - davon gab es zwei, jetzt vier (Haupt, zweite, Fusszeile, Rechtliches). Wie es aussieht, war gar nicht einstellbar: jetzt waagerecht, untereinander oder aufklappbar als Burger. Der Burger ist ein details-Element, denn ein Menue, das ein Skript zum Oeffnen braucht, geht auf einer Seite ohne Skript nicht auf. "Seiten ohne Menue" war nicht moeglich - der Kopfbereich war ein Dokument fuer alle Seiten. Jetzt waehlt jede Seite: Standard, ein anderer Bereich, oder keiner. Dafuer musste die Regel "es gibt genau einen Kopf- und Fussbereich" fallen; sie war richtig, solange niemand waehlen konnte, und wurde falsch, sobald man waehlen kann. Ein bestehender Test, der diese Regel zusicherte, ist deshalb umgeschrieben und prueft jetzt die neue. Gespeichert wird die Wahl, nicht das Ergebnis: leer heisst Standard, none heisst keiner, sonst die Adresse. Ein Verweis auf einen geloeschten Bereich faellt auf den Standard zurueck, statt die Seite stillschweigend ohne Navigation auszuliefern. Neu im Ausbauplan: ein einzelnes install.php, das das Releasearchiv herunterlaedt und entpackt, damit niemand tausende Dateien per FTP hochladen muss. Mit den Regeln, die darueber entscheiden, ob das eine Hilfe oder eine Einladung ist: Adresse und SHA-256 stehen in der Datei und nicht im Formular, HTTPS mit Zertifikatspruefung, Schutz gegen ZIP-Slip, Selbstloeschung danach. 336 PHP-Tests / 1354 Assertions, 17 JS-Modelltests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
67b85751cf |
Randlose Abschnitte, volle Bildschirmhoehe, Vollbild-Hero
Umuts Frage: "Haben wir bilder usw als volles bild startseite? 100% hoehe usw?" Die Teile gab es, die Kombination nicht - und zwei Sachen fehlten wirklich. Abschnittsbreiten: neu "randlos" (ohne Begrenzung, ohne Rand, von Kante zu Kante) und "breit" (1600 px) neben normal und schmal. Vorher war jeder Inhalt auf 1180 px begrenzt; ein Bild von Kante zu Kante war nicht moeglich. Hoeheneinheiten: neu dvh, svh, lvh. 100vh ist auf dem Handy hoeher als der Bildschirm - es meint die Ansicht ohne Adressleiste, und der untere Rand verschwindet darunter. 100dvh ist das, was jemand meint, der "volle Hoehe" sagt. Und eine Vorlage "Vollbild-Hero", die die ganze Kombination auf einen Klick setzt: randlos, 100dvh (mobil 80dvh), Inhalt senkrecht zentriert, Hintergrundbild formatfuellend, dunkle Ueberlagerung damit der Text lesbar bleibt, Ueberschrift, Text, Button. Die Teile waren alle da, aber diese Kombination zu finden war eine Recherche. 328 PHP-Tests / 1329 Assertions, 17 JS-Modelltests. Geprueft: vier Breiten angenommen und eine erfundene abgewiesen, sieben Hoehenangaben mit dvh/svh/lvh und zwei ungueltige, ein randloser Abschnitt mit formatfuellendem Bild, und die Hero-Vorlage Eigenschaft fuer Eigenschaft. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
7251b82f09 |
Designer: 93 Eigenschaften, neun Einblendungen, drei neue Bausteine
Umut fragte nach Zeilen, nach Rahmen und Rundungen - und ob der Designer so aufgestellt ist wie Flatsome. Der Vergleich steht in docs/DESIGNER-VERGLEICH.md, mit der Einschraenkung, dass Flatsome hier nicht installierbar war: das ist Wissen, keine Messung. Zwei echte Fehler kamen dabei heraus, beide vorher unbemerkt. Eine Rahmenstaerke je Seite wurde zu border-width-top. Das ist kein CSS, der Browser verwirft die Zeile stillschweigend - Rahmenstaerken je Seite hatten nie eine Wirkung, und es gab keine Meldung, die darauf hingewiesen haette. Die Schreibweise steht jetzt je Eigenschaft im Schema, weil padding-top dem Namen folgt und border-top-width nicht. Und eine Rundung auf einem Bildblock landete auf dem figure, waehrend das Bild darin eckig blieb. Eigenschaften koennen jetzt auf das Bild im Block zielen (target 'img' im Schema, eine zweite Regel im Compiler) - deshalb tut object-fit ueberhaupt etwas, denn auf einem figure tut es nichts. Von 50 auf 93 Eigenschaften: Bild (Ausschnitt, Bildmitte, Seitenverhaeltnis, Hoehe, Ecken, Rahmen), Positionierung mit z-index, getrennte Zeilen- und Spaltenabstaende, Flex je Element, Innen- und Textschatten, acht Bildfilter als eine filter-Deklaration in fester Reihenfolge, Mischmodus, Zusatzrahmen, Mauszeiger. Neun Einblendungen mit Verzoegerung, sieben Hover-Effekte, alle innerhalb von prefers-reduced-motion. Drei neue Bausteine (Dokumentschema v15): Symbol als eigenes Element, Fortschritt mit role=meter und dem Wert im Markup statt nur in der Balkenbreite, und ein Slider als Scrollbereich mit Einrasten - ohne JavaScript, ohne Autoplay, mit Finger, Trackpad, Mausrad und Tastatur. Zeilen waren nicht unauffindbar, sondern unbrauchbar leer: eine Zeile ohne Spalten sieht aus wie nichts. Jetzt legt "Zeile, 2/3/4 Spalten" beides auf einen Klick an, und im Werkzeugkasten steht, wie der Aufbau gemeint ist. 315 PHP-Tests / 1309 Assertions, 16 JS-Modelltests, axe auf dreizehn Bildschirmen ohne Verstoesse. 16 Datensaetze rechnen jede Eigenschaftsform gegen die erwartete Deklaration nach - die Pruefung, die die beiden Fehler oben gefunden haette, wenn es sie vorher gegeben haette. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
ee446f1ea1 |
Einwilligung, die tatsächlich zurückhält, und Pflichtseiten
Ein Hinweis, der nur fragt und sonst nichts ändert, ist Theater. Deshalb zwei Hälften, und die zweite ist die wichtigere. Die Frage: serverseitig gerendert, per Formular beantwortet, ohne JavaScript - die öffentlichen Seiten laufen mit script-src 'none', und ein Banner mit Skript wäre das eine, was diese Regel bricht. "Alle akzeptieren" und "Nur notwendige" sind dieselbe Art Knopf, gleich groß, nebeneinander. Die Entscheidung steht in einem eigenen Cookie: Fassung der Frage, gewährte Gruppen, Zeitpunkt. Keine Kennung, kein Serverabgleich, ein halbes Jahr. Kein CSRF-Token, absichtlich: die Anfrage ändert nichts auf dem Server, und ein Token bräuchte für jeden Besucher eine Sitzung - genau das, was ein Einwilligungshinweis vermeiden soll. Geprüft wird stattdessen das Rücksprungziel. Das Zurückhalten: der neue Block "Externer Inhalt" (Dokumentschema v14) lädt ohne Einwilligung nichts - kein iframe, keine Adresse im Markup, frame-src 'none'. Der Platzhalter nennt den Anbieter; sein Knopf gewährt genau diese Gruppe und nimmt die vorher gewährten mit. Erst danach kommt der Host in die Sicherheitsrichtlinie, und nur dieser. Im Editor bleibt es immer ein Platzhalter. Datenschutz und Impressum sind keine Einstellung mehr: fehlen sie, warnt die Verwaltung und legt sie auf Knopfdruck als Gerüst an, mit NOCH AUSZUFÜLLEN an jeder Pflichtangabe. Den Text schreibt das CMS nicht - das ist eine rechtliche Aussage über eine Firma. Verlinkt werden beide aus dem Template, nicht aus dem Fußbereich: ein Pflichtlink, den man löschen kann, ist keiner. Dazu der Fehler, den Umut im Screenshot gesehen hat: die Wortmarke stand als Buchstabenkolonne in einem 60 Pixel breiten Kasten. .site-header war noch ein Flexcontainer aus der Zeit des handgeschriebenen Markups und quetschte die Sektion zusammen, und design.css band jede Designklasse an #main - wo Kopf- und Fußbereich seit ADR 026 nicht mehr liegen. Beides behoben. Neu im Ausbauplan: Statistik (serverseitige, wiedererkennungsfreie Zählung, die deshalb keine Einwilligung braucht), Inhalte und Ladeverhalten prüfen (Blättern überall, minimal laden, Ladebudget), Installer (Schritt für Schritt im Browser, Migration inklusive). 245 PHP-Tests / 976 Assertions unter PHP 8.2.12 und 8.4.22. Der Browsertest fand zwei Fehler von mir: grauer Text auf dunklem Fußbereich mit 2,24:1, und das fixierte Banner verdeckte den Einwilligungsknopf am Seitenende. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
a22a7f7fd7 |
Eigenes CSS je Element für alle, eigenes Skript nur für Administratoren
Dokumentschema v13. Der neue Reiter "Erweitert" im Editor nimmt zu jedem
Element eigenes CSS und eigenes Skript auf.
Eigenes CSS parst `CustomCode` und schreibt es selbst wieder aus, mit einem
Selektor, den die Klasse setzt und der nie aus der Eingabe stammt. Erlaubt
sind Eigenschaften für das Element und verschachtelte Regeln mit `&`
(`&:hover`, `& > p`, `& .block-text`). Weil neu serialisiert wird, kann eine
überzählige `}` den Block nicht verlassen. Abgewiesen: @-Regeln,
Backslash-Escapes, `expression()`, fremde Selektoren und jede Adresse außer
einem einzelnen `/` oder `https://`. Das darf jeder mit Editorrolle.
Eigenes Skript ist etwas anderes: beliebiges JavaScript auf der
veröffentlichten Website. Deshalb nur für Administratoren, durchgesetzt beim
Schreiben über einen Vergleich mit dem gespeicherten Stand - ein Editor kann
eine Seite mit Skript weiter bearbeiten, das Skript aber nicht anfassen, auch
nicht über eine selbst gebaute Anfrage. Ausgeliefert wird es als
`/assets/seite/{id}.js` und nur für veröffentlichte Seiten, jedes Skript in
einer eigenen Closure mit gesuchtem `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.
Zwei echte Löcher kamen beim Testen heraus und sind behoben: `& .block-text`
wurde zu `#id.block-text`, weil das führende Leerzeichen wegtrimmt wurde, und
`url(//fremd.example/x.png)` kam durch, weil jede Adresse mit `/` erlaubt war.
201 PHP-Tests / 716 Assertions unter PHP 8.2.12 und 8.4.22, Playwright-
Editortest und öffentliche Browserprüfung grün. Nicht geprüft: der Reiter
"Erweitert" ist nicht von Hand bedient und nicht mit einem Screenreader
getestet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
790f42213c |
Formatierter Text mit Werkzeugleiste und HTML-Ansicht (Schema v12)
Textblöcke dürfen ab v12 Auszeichnung tragen: fett, kursiv, unterstrichen, durchgestrichen, Code, hervorgehoben, hoch- und tiefgestellt, Absätze, Listen und Links. Die Entscheidung, was erlaubt ist, fällt auf dem Server beim Speichern - nicht im Editor und nicht beim Rendern. Was in der Datenbank landet, ist damit schon sicher, also kann kein Renderer das Filtern vergessen, und eine manipulierte Anfrage speichert nichts, was der Editor nicht anbietet. Nicht erlaubte Elemente werden ausgepackt, nicht gelöscht: der Text bleibt, nur die Auszeichnung geht. script, style, iframe und Verwandte verschwinden samt Inhalt, weil Auspacken ihren Code als Text sichtbar machen würde. Attribute fallen weg außer href an a, und auch das nur nach Adressprüfung. Ausgegeben wird formatierter Text nur ab v12, dann in einem div statt einem p, weil die Positivliste Absätze und Listen enthält. Ältere Dokumente behalten Klartextsemantik und werden weiter maskiert. Eine Inkonsistenz nebenbei behoben: ein Text mit einem < behielt echte Zeilenumbrüche, während alles andere <br> benutzte. Geprüft: 182 PHP-Tests / 685 Assertions unter PHP 8.2.12 und 8.4.22, darunter dreizehn Fälle durch den Filter mit acht Angriffen, dazu Editortest mit axe und öffentliche Browserprüfung. Die Werkzeugleiste selbst ist nicht bedient und nicht mit einem Screenreader geprüft. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
d0938eec69 |
Kopf, Fuß und verknüpfte Sektionen als Blockdokumente (Paket G, Teil 1)
Migration 20260912001000 legt die Tabelle partial an: Blockdokumente, die keine Seiten sind. Kopf- und Fußbereich je einmal, wiederverwendbare Sektionen beliebig viele - im selben Editor gestaltbar wie eine Seite, weil der Editor seine Schnittstelle aus einem Datenattribut nimmt statt sie fest zu kennen. Im Template steht keine Marke und keine Navigation mehr. Ein neuer Block menu zeigt einen Menüplatz; die Einträge bleiben in der Menüverwaltung. Eine Seite braucht genau eine Hauptüberschrift, ein geteilter Bereich keine - sonst hätte jede Seite, die ihn trägt, zwei. Geteilte Bereiche haben keinen Entwurf und keine Veröffentlichung: sie sind überall sichtbar, gespeichert heißt live. Verknüpft statt kopiert: partialref speichert nur die Adresse, die Blöcke bleiben im Bereich, und eine Änderung erreicht jede Seite, die sie einbindet. Zyklen abgewiesen, Verschachtelung auf drei Ebenen begrenzt, eine entfernte Sektion hinterlässt keine Lücke und der Verweis bleibt erhalten. Die Abschnittsvorlagen kopieren weiterhin. Ein echter Fehler kam dabei heraus, der jede dunkle Sektion mit Spalten betraf: .block-column>.block-text:first-child setzt eine Farbe für helle Hintergründe und gewinnt auf Spezifität gegen .tone-ink - gemessen 1,87:1. Geprüft: 165 PHP-Tests / 660 Assertions unter PHP 8.2.12 und 8.4.22, 15 JavaScript-Modelltests, Editortest mit axe ohne Verstöße, öffentliche Browserprüfung, Migration nach Backup angewendet. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
abf0bd2c6b |
Freie Gestaltung statt Tokenlisten (Dokumentschema v10)
Der bisherige Editor bot drei Farben, keine Schriftwahl und elf Auswahllisten mit 44 festen Werten. ADR 008 hatte das mit Sicherheit begründet - der Schluss war falsch: die Alternative zu beliebigem CSS-Text ist nicht eine Auswahlliste, sondern ein typisiertes Designmodell. StyleSchema erklärt jede Eigenschaft einmal mit Typ, CSS-Entsprechung und Grenzen. Validator, CSS-Erzeugung und Editorpanel lesen dieselbe Definition, also kann der Editor nichts anbieten, was der Server ablehnt. Rund fünfzig Eigenschaften in sieben Gruppen, je Block, je Gerät und für den Hover-Zustand, mit Vererbung und einzelnem Zurücksetzen. Werte sind kurze Zeichenketten mit verankertem Muster, nie CSS-Text; zusammengesetzte Deklarationen baut der Compiler selbst. Der Server erzeugt daraus pro Seite ein blockgenaues Stylesheet, das über seinen eigenen SHA-256-Hash erlaubt wird statt über unsafe-inline. Theme unter /admin/theme: freie Farbpalette als @schlüssel, Schriften aus Systemschrift oder Google Fonts, Grundtypografie mit modularer Überschriftenskala. Google Fonts auf ausdrückliche Nutzerentscheidung; die Richtlinie öffnet die Hosts nur, wenn wirklich eine eingestellt ist. Alte Tokenwerte aus v2 bis v9 bleiben gültig und im Editor unter "Klassische Werte dieser Seite" bearbeitbar. Geprüft: 154 PHP-Tests / 563 Assertions unter PHP 8.2.12 und 8.4.22, 15 JavaScript-Modelltests, Lint/Format/Container. Im Browser wurde nur der Aufbau des Panels bestätigt, kein Gestaltungsvorgang von Hand ausgeführt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
a40c2bb27e |
SEO, Weiterleitungen und strukturierte Daten (Arbeitspaket E)
Migration 20260912000800 ergänzt page.robots, page.og_media_id, website.settings und die Tabelle redirect. Alles Maschinenlesbare wird aus dem veröffentlichten Bestand abgeleitet, nichts separat gepflegt: die Sitemap listet nur veröffentlichte, indexierbare Seiten samt Beitragsübersicht und Begriffsarchiven, und dieselbe Einstellung setzt auch das noindex-Meta - beide Aussagen können nicht auseinanderlaufen. Strukturierte Daten entstehen nur aus gepflegten Werten: ohne Angaben wird kein leerer Organization-Block ausgegeben. JSON-LD ist ein Datenblock, kein Skript, daher bleibt script-src 'none' unberührt. Weiterleitungen greifen vor dem 404, aber nach der Seitensuche: eine vorhandene Seite gewinnt immer, sonst könnte ein alter Eintrag eine neue Seite unerreichbar machen. Ketten werden abgewiesen. robots.txt nimmt zusätzliche Zeilen nur nach festem Muster an, damit ein Eintrag die Datei nicht umdeuten kann. llms.txt ist standardmäßig aus. Geprüft: 128 PHP-Tests / 507 Assertions unter PHP 8.2.12 und 8.4.22, 14 JavaScript-Modelltests, Lint/Format/Container. Dazu eine reine Sichtprüfung im Browser auf einer lokalen Demoseite - weiterhin keine axe- oder Lighthouse-Messung. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
d43bd91069 |
Formulare mit Validierung, Einwilligung und Spamschutz (Arbeitspaket D)
Migration 20260912000700 legt form und form_submission an. Der Formularblock im Seiteninhalt enthält nur die Adresse: Felder, Empfänger und Einwilligungstext liegen in der Datenbank, damit ein gespeichertes Dokument nicht ändern kann, wonach gefragt wird oder wohin eine Anfrage geht. Serverseitige Validierung mit einer Meldung je Feld über aria-describedby und aria-invalid; eine Ablehnung rendert die Seite mit HTTP 422 und allen Eingaben neu, eine gültige Absendung wird mit HTTP 303 zurückgeleitet. Alles ohne JavaScript. Spamschutz ohne Dienst Dritter: Honeypot, signierter Zeitstempel gegen zu schnelle und zu alte Absendungen, fünf Absendungen je Absender und Stunde. Ein erkannter Bot bekommt dieselbe Antwort wie ein Erfolg. Die Absenderadresse wird nie gespeichert, nur ein Hash. Gespeichert wird vor dem Versand, damit ein kaputter Mailversand keine Anfrage verliert. Kein CSRF-Token auf dem öffentlichen Weg, weil das eine Sitzung und damit ein Cookie auf jeder öffentlichen Seite bedeuten würde; Begründung in ADR 017. Dateiuploads fehlen bewusst. Ein echter Fehler von den Tests gefunden: das Stundenfenster des Limits entstand aus lokaler Zeit, die Zeitstempel aber aus UTC - auf einer Uhr vor UTC hätte das Limit nie gegriffen. Geprüft: 117 PHP-Tests / 450 Assertions unter PHP 8.2.12 und 8.4.22, 14 JavaScript-Modelltests, Lint/Format/Container, composer audit. Der E-Mail-Versand wurde nie wirklich ausgeführt; keine Browser- oder Lighthouse-Läufe. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
c6ec00a906 |
Tabs als fortschreitende Verbesserung (Dokumentschema v7)
Auf Entscheidung von Umut werden Tabs jetzt mit JavaScript umgesetzt. Die Regel "der Seiteninhalt braucht kein Browser-JavaScript" bleibt dabei eingehalten: der Server liefert eine Ankerliste und alle Panels mit eigenen Überschriften, versteckt nichts, und ohne Skript ist jeder Inhalt lesbar und über die Tastatur erreichbar. public/assets/tabs.js ergänzt tablist/tab/tabpanel, Auswahl per Pfeiltasten, Home und Ende sowie aria-selected und wählt bei einem passenden Sprungziel das zugehörige Panel. Die strenge Richtlinie bleibt Voreinstellung: öffentliche Seiten senden weiterhin script-src 'none'. Nur eine Seite mit Tab-Block sendet script-src 'self' und bindet die eine externe Datei ein; Inline-Skript ist nie erlaubt. Geprüft: 109 PHP-Tests / 395 Assertions unter PHP 8.2.12 und 8.4.22, 14 JavaScript-Modelltests, Lint/Format/Container. Das Skript selbst wurde nicht ausgeführt: Tastaturbedienung und ARIA-Verhalten sind ohne Browserlauf ungeprüft. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
8600384f1a |
Zehn Blöcke für die Abschnitte der Pilotseite (Arbeitspaket C)
Dokumentschema v6 ergänzt iconcard, stat, quote, logos, cta, contact, divider, spacer, accordion und accordionitem - die Abschnittsarten, aus denen die Startseite von kant-it.de besteht. Symbole liegen als Inline-SVG aus einer festen Liste im Template: kein Symbolschriftsatz, keine externe Anfrage. Kontaktdaten werden zu tel:- und mailto:-Verweisen mit bereinigter Nummer. Das Akkordeon nutzt details/summary und braucht damit kein öffentliches JavaScript. Tabs sind bewusst nicht umgesetzt: ohne JavaScript gibt es dafür keine zugängliche Lösung, und eine CSS-Variante über Radiobuttons wird als Auswahlfeld statt als Tabliste angekündigt. Begründung in ADR 015. Zwei Fehler von den Tests gefunden und behoben: die Regel "ein Akkordeon-Eintrag gehört in ein Akkordeon" griff nicht auf oberster Dokumentebene, und ein JavaScript-Test suchte im vorherigen Dokumentstand, weil history.document vor history.change ausgewertet wurde. Geprüft: 107 PHP-Tests / 380 Assertions unter PHP 8.2.12 und 8.4.22, 13 JavaScript-Modelltests, Lint/Format/Container. Keine Browser- oder Lighthouse-Läufe; für die neuen Blöcke liegt damit keine visuelle Kontrolle und keine Barrierefreiheitsprüfung vor. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
ebd5883593 |
Beiträge mit Archiven, Beitragsliste und Feed (Arbeitspaket B)
Migration 20260912000600 ergänzt page.excerpt, author,
featured_media_id und published_at sowie die Tabellen term und
page_term. Beitragsmetadaten beschreiben die Seite, nicht ihren
Inhalt, und liegen deshalb als Spalten statt im Blockdokument: das
Archiv sortiert und paginiert damit in SQL, statt für jede
Auflistung alle Dokumente zu dekodieren.
Kategorien und Schlagwörter teilen sich eine Tabelle mit einem
kind-Feld. Beim Löschen einer Seite verschwinden ihre Zuordnungen,
die Begriffe bleiben als wiederverwendbare Etiketten; ein leer
gewordenes Archiv antwortet weiter mit 200 und leerer Liste.
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. Archive unter /beitraege, /kategorie/{slug} und
/schlagwort/{slug}, Feed unter /feed.xml; alle vier Segmente sind
für Wurzelseiten reserviert.
Geprüft: 91 PHP-Tests / 347 Assertions unter PHP 8.2.12 und 8.4.22,
12 JavaScript-Modelltests, Lint/Format/Container, Migration zuerst
gegen eine Datenbankkopie. Keine Browser- oder Lighthouse-Läufe.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
8e0006f883 |
Unbekannte Blöcke erhalten statt abweisen (Dokumentschema v4)
Der Lesevalidator wies bisher jeden unbekannten Blocktyp ab, womit der Fallback im Renderer unerreichbar war: eine Seite mit Bausteinen aus einer deaktivierten Erweiterung wäre nicht mehr ladbar gewesen. Ab Schema v4 gilt ein Typ mit Namensraum (acme.slider) als Baustein einer Erweiterung und bleibt erhalten. Der Kern prüft weiterhin ID, Tiefe, Blockanzahl und Dokumentgröße, aber bewusst nicht die Props - er kennt das Schema der Erweiterung nicht. Damit ungeprüfte Werte nie ausgegeben werden, rendert der Kern aus einem solchen Block nichts: öffentlich nur ein HTML-Kommentar, in der Editorvorschau ein Platzhalter. Im Editor bleiben die Blöcke auswählbar, verschiebbar, duplizierbar und löschbar, aber nicht bearbeitbar, und es lässt sich nichts in sie einfügen. Testumgebung: Twig lädt Vorlagen jetzt automatisch neu. Ohne Debug konnte eine kompilierte Vorlage im Cache den Ausgang eines Testlaufs bestimmen. Geprüft: 64 PHP-Tests / 229 Assertions unter PHP 8.2.12 und 8.4.22, 11 JavaScript-Modelltests, Lint/Format/Container. Keine Browser- oder Lighthouse-Läufe. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
82b07820bc |
Kant CMS: Grundgerüst, visueller Editor, Mediathek
Symfony 7.4 / Twig / Doctrine DBAL auf herkömmlichem PHP-Webhosting. Versionierte Blockdokumente (v1-v3) mit unveränderlichen Revisionen, getrennten Entwürfen/Veröffentlichungen und optimistischer Konflikterkennung. Visueller Editor mit Strukturbaum, Live-Vorschau, responsiven Gerätewerten, Abschnittsvorlagen, Bildmediathek, Galerien und Effekten. Öffentliche Ausgabe ohne Editor-JavaScript. Geprüft: 41 PHP-Tests / 155 Assertions, 10 JavaScript-Modelltests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |