Files
KantSites/docs/NEUE-WEBSITE.md
T
kantarciandClaude Opus 5 d6d476177a
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
Fassung 1.4.3: Update ohne Git, Verwaltung auf dem Telefon, eigene Symbole
Vier Wuensche und ein Fund beim Nachmessen.

- Farbwaehler im Designbereich. Das Textfeld bleibt massgeblich, damit
  "@akzent" ein Verweis auf die Palette bleibt und nicht beim ersten
  Klick zu einem festen Hexwert erstarrt (ADR 066).
- Die Verwaltung auf dem Telefon: das Menue klappt unter 760 px zu und
  nennt den Bereich, in dem man steht; Ziele 44 px, kein Skript.
- Eigene SVG-Symbole statt Unicode-Zeichen - ein Telefon ersetzt die
  sonst durch Emoji in fremdem Stil. Zwei Eintraege trugen dabei
  denselben Pfad; ein Test findet beides (ADR 067).
- Update aus dem Releasearchiv: der Weg fuer Installationen ohne Git.
  Zwei Schritte, weil ein Prozess, der sich selbst ausgetauscht hat,
  mit dem alten Code weiterarbeitet. var/, .env, hochgeladene
  Schriften und das erzeugte Buendel bleiben unberuehrt; public/ wird
  dazu Datei fuer Datei zusammengefuehrt. Geprueft ohne Netz gegen
  eine Releaseseite im Speicher (ADR 068).

Beim Nachmessen der echten Startseite statt einer Testseite: das
groesste Element ueber der Falz ist dort kein <img>, sondern der
Hintergrund des ersten Abschnitts - und der steht nur im Stylesheet.
Drei Vorabverweise mit denselben Geraetegrenzen wie der Compiler holen
genau die Variante, die die Regel auch nimmt. LCP 1,3 s -> 1,1 s.
Dabei gefunden: dasselbe Bild wurde je Geraeteregel neu abgefragt -
Startseite 19 -> 15 Abfragen (ADR 069).

Geprueft: 428 Tests auf PHP 8.2 und 8.4, Editor-, Browser- und
Linkpruefung, Lighthouse auf drei echten Seiten (alles ausser der
Leistung der Startseite 100).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 23:01:32 +02:00

11 KiB
Raw Blame History

Eine neue Website mit KantSites bauen

Diese Anleitung richtet sich an den, der die Arbeit macht – Mensch oder Assistent. Sie sagt, in welcher Reihenfolge etwas entsteht, welche Entscheidung wohin gehört und welche Fehler dieses Projekt schon einmal gemacht hat. Wer sie befolgt, kommt ohne Umwege zu einer Website, die die Prüfungen besteht.

Lies zuerst: AGENTS.md (Projektregeln), docs/STATUS.md (was es gibt und was fehlt), docs/ARCHITECTURE.md (warum es so ist). Diese drei sind der Stand der Dinge; diese Datei hier ist das Kochbuch.


0. Die Grundregel

Alles, was eine Website ausmacht, steht in der Datenbank – nicht im Code. Seiten, Kopf- und Fußbereich, Menüs, Formulare, Farben, Schriften, Einstellungen. Wer für eine neue Website eine Twig-Datei anfasst oder CSS in public/assets/site.css schreibt, hat sich verlaufen: dann ändert sich das Aussehen aller Installationen, und beim nächsten Update ist es weg.

Ausnahme mit Ansage: eigenes CSS und eigenes Skript je Seite gibt es im Editor (Bereich „Code"), und es landet ebenfalls in der Datenbank.


1. Installation aufsetzen

# Eigene Datenbank und eigener Medienordner - nie gegen die Entwicklungsinstallation arbeiten.
DATABASE_URL="sqlite:///…/var/kunde.db" php vendor/bin/doctrine-migrations migrate --no-interaction
DATABASE_URL="sqlite:///…/var/kunde.db" MEDIA_DIRECTORY="…/var/kunde-media" php bin/console cms:user:create

Oder im Browser über /install – sechs Schritte, legt Startseite, Kopf, Fuß, Menü, Impressum und Datenschutzgerüst gleich mit an.

Die Verwaltung muss nicht /admin heißen. ADMIN_PATH in .env setzt die Adresse; php bin/console cms:admin:path <neu> prüft, ändert und sagt, welcher Zwischenspeicher danach zu leeren ist. Bei einer Kundenwebsite lohnt das: /admin ist die erste Adresse, die ein Scanner probiert.

Zum Ansehen ein eigener Server auf einem eigenen Port:

DATABASE_URL="sqlite:///…/var/kunde.db" MEDIA_DIRECTORY="…/var/kunde-media" \
  SITE_ORIGIN=http://127.0.0.1:8090 php -S 127.0.0.1:8090 -t public bin/dev-router.php

bin/dev-router.php ist Pflicht: ohne ihn beantwortet der eingebaute Server /sitemap.xml und /robots.txt selbst, und die Dateien kommen ohne die Kopfzeilen, die später public/.htaccess setzt – jede Messung wäre dann falsch.


2. Inhalte anlegen: als Skript, nicht von Hand

Eine vollständige Website besteht aus Dutzenden Blöcken mit Hunderten Gestaltungswerten. Das klickt niemand zweimal. Schreibe ein Aufbauskript, das Medien hochlädt, Dokumente zusammensetzt und veröffentlicht – Vorlage ist das Skript, mit dem kant-it.de nachgebaut wurde.

Drei Eigenschaften, die ein solches Skript haben muss (alle drei sind aus Schaden gelernt):

  1. Wiederholbar. Ein zweiter Lauf aktualisiert vorhandene Seiten, statt sie zu überspringen – sonst ändert eine Korrektur im Skript nichts an der Website. Bilder werden am Titel wiedererkannt, sonst wächst die Mediathek mit jedem Lauf.
  2. Eine Quelle für Adressen. Verweise nie von Hand schreiben. Die Adresse eines Beitrags entsteht aus derselben Funktion, die den Beitrag anlegt. Im Nachbau standen vier Links im Hero von Hand darin – alle vier tot, weil die Beiträge längere Adressen bekamen.
  3. Über die Repository-Klassen, nicht per SQL. PageRepository::saveDraft() und publish() prüfen das Dokument. Ein per INSERT eingeschmuggeltes Dokument kann Felder enthalten, die der Editor nie wieder öffnen kann.

Reihenfolge im Skript: Theme → Medien → Kopf- und Fußbereich → Seiten → Beiträge → Menüs → Formulare → Einstellungen.


3. Das Dokumentformat

Ein Dokument ist {schemaVersion, title, description, blocks: [...]}. Ein Block ist {id, type, props, style, layout, children}.

  • id – höchstens 64 Zeichen, eindeutig im Dokument, erscheint als HTML-id und als CSS-Selektor. Sprechend wählen (hero-headline, nicht block-7).
  • type – siehe Liste im Editor; section, row, column, heading, text, image, button, postlist, slider, tabs, form, contact, stat, quote, iconcard, …
  • props – der Inhalt. Was hier steht, ist Text und Daten, nie Auszeichnung mit Wirkung.
  • style – {desktop: {...}, tablet: {...}, mobile: {...}}, jeder Wert aus StyleSchema. Seiten, Abstände und Ecken sind Objekte (['top' => '10px', …]), keine Kurzschreibweise.
  • layout.effects – {entrance: 'rise', delay: 'short'} für das Einblenden beim Scrollen.

Setze schemaVersion auf die aktuelle Fassung (siehe DocumentValidator::SCHEMA_VERSION), sonst sind neuere Bausteine und Eigenschaften nicht erlaubt – und der Editor kann in einem zu alten Dokument nichts einfügen.


4. Gestaltung

Erst das Theme, dann die Blöcke. Farben und Schriften gehören in die Palette (/admin/theme): ein Block, der @accent sagt, ändert sich mit dem Theme; einer, der #c1121f sagt, nicht. Die Palette färbt auch Knöpfe, Symbole, Fokusringe und den Einwilligungsdialog.

Jedes Farbfeld hat einen Farbwähler und die Palettenfarben zur Auswahl. Das Textfeld daneben bleibt maßgeblich: wer dort @accent stehen lässt, behält den Verweis auf die Palette; wer im Farbwähler zieht, schreibt einen festen Wert hinein und kappt ihn.

Feste Größen nur, wo sie gemeint sind. maxWidth ohne margin: auto zentriert nichts – das war hier schon zweimal die Ursache eines „geht nicht".

Die Vorschau im Editor zeigt dieselbe Ausgabe wie die Website. Was dort falsch aussieht, ist falsch – nicht „nur die Vorschau".


5. Bilder

  • Hochladen über MediaLibrary::upload(); daraus entstehen WebP-Varianten in 480/960/1600/2400 px (nie größer als das Original). Das Original wird nicht behalten.
  • Vor dem Hochladen prüfen: über 12 Megapixel oder 8000 px Kantenlänge werden abgelehnt, und ein Hostingpaket mit 128 MB Speicher schafft große Bilder nicht. Vorher auf 2400 px verkleinern.
  • Im Dokument immer alle sources mitgeben, damit srcset entsteht.
  • Ein Bild mit fester Anzeigebreite bekommt style.desktop.maxWidth – daraus entsteht das sizes-Attribut, und der Browser lädt die kleine Variante. Ohne das lud ein 190-px-Logo die 960er Datei.
  • Hintergrundbilder werden je Gerät verkleinert, auch wenn sie nur einmal gesetzt wurden.
  • „Beim Klick" entscheidet je Bild, ob nichts passiert, ob es sich vergrößert oder ob es zu einer Adresse führt; eine Galerie hat dafür einen Schalter. Zugrunde liegt ein gewöhnlicher Verweis auf die große Fassung – ohne JavaScript öffnet ein Klick sie, nichts ist kaputt.

6. Beiträge

Optional. Gibt es keine, verschwinden Archiv, Feed und Feedverweis von selbst; ein Beitragslisten-Baustein zeigt öffentlich nichts.

Beim Anlegen wählt man einen von drei Aufbauten (PostLayouts: Bildkopf, Klassisch, Magazin), für die Liste eine von vier Darstellungen (Karten, Liste, Kompakt, Wechselseitig). Beides sind Startpunkte, keine Schablonen.


7. SEO

Meistens fertig, wenn der Inhalt stimmt: Titel, Beschreibung, Canonical, Open Graph, strukturierte Daten, Sitemap, robots.txt entstehen aus dem, was dasteht. Zu setzen bleibt:

  • Titel für Suchergebnisse je Seite, wenn er sich vom Titel auf der Seite unterscheiden soll.
  • Vorschaubild beim Teilen – sonst nimmt ein Beitrag sein Beitragsbild und eine Seite gar nichts.
  • Unternehmensdaten unter Einstellungen; daraus entsteht der strukturierte Datensatz.
  • Weiterleitungen, wenn die Website eine alte ablöst. Vor dem Umzug die Adressliste der alten Website besorgen (deren sitemap.xml) und für jede Adresse, die sich ändert, eine Weiterleitung anlegen. Ohne das ist der Rang von Jahren weg.

8. Schriften

Nach dem Aufsetzen einmal php bin/console cms:fonts:fetch (oder der Knopf im Designbereich). Ohne das lädt die Website die Schriften von Google – zwei fremde Verbindungen nacheinander und die IP-Adresse jedes Besuchers an einen Dritten. Die Dateien der Pilotwebsite liegen im Repository; wählt eine neue Website andere Schriften, holt dieser Befehl sie.


9. Prüfen

In dieser Reihenfolge, gegen die neue Website:

CMS_URL=http://127.0.0.1:8090 npm run test:links     # kein toter Verweis
CMS_URL=http://127.0.0.1:8090 npm run test:browser   # Barrierefreiheit, Tastatur, ohne JavaScript, Ladebudget
MSYS_NO_PATHCONV=1 CMS_URL=http://127.0.0.1:8090 CMS_PATHS="/,/leistungen,/kontakt" npm run audit

Für npm run audit muss die Installation im Produktionsmodus laufen (APP_ENV=prod, APP_DEBUG=0), sonst misst man den Debugmodus.

Am Code geändert? Dann zusätzlich php vendor/bin/phpunit (beide PHP-Fassungen), npm run test:editor und composer format.

Zielwerte: Barrierefreiheit, Best Practices und SEO je 100. Performance mindestens 95, 100 anstreben – eine bilderreiche Startseite mit Slider landet realistisch bei 96–98.

Findet die Prüfung einen Kontrastmangel aus einer im Editor gesetzten Farbe, wird er gemeldet und nicht behoben: das ist eine Entscheidung des Redakteurs, keine des Codes.


10. Livegang

docs/LIVEGANG.md ist die Liste. Die drei Dinge, die dort am häufigsten vergessen werden: E-Mail-Versand einmal wirklich ausprobieren, Schriften holen, und die Weiterleitungen der alten Adressen prüfen.


11. Updates dieser Installation

Zwei Wege, und welcher gilt, entscheidet das Hosting:

  • Mit Git (eigener Server, Shell-Zugriff): Bildschirm „System" oder php bin/console cms:update. Vorprüfung, Sicherung, Wartungszustand, Checkout, Migration, Rauchtest, Rückweg.
  • Ohne Git (geteiltes Hosting, über install.php installiert): im selben Bildschirm die Karte „Update aus dem Releasearchiv" – zwei Knöpfe, der zweite führt die Migrationen aus. var/, .env, hochgeladene Schriften und das erzeugte Stylesheet-Bündel bleiben unberührt.

Beides steht ausführlich in docs/UPDATE.md. Wichtig für den, der eine Website baut: nichts von dem, was diese Website ausmacht, liegt im Code, also überlebt es beide Wege. Wer dafür doch eine Twig-Datei angefasst hat, verliert die Änderung beim ersten Update – siehe Abschnitt 0.


Was in diesem Projekt immer wieder schiefging

Wer diese fünf kennt, spart sich die Suche:

  1. Eine Einstellung, die speichert und nichts tut. Das Feld war da, die Ausgabe nicht – beim Vorschaubild fürs Teilen zwei Monate lang. Nach jedem neuen Feld nachsehen, ob es vorne ankommt, und einen Test schreiben, der genau das prüft.
  2. |default(true) in Twig macht aus einem gespeicherten false ein true. Für Schalter immer ?? verwenden.
  3. Die Prüfung lief gegen die Demo, nicht gegen die Website. Feste Pfade und Linktexte in einem Prüfskript prüfen nichts, sobald der Inhalt anders ist.
  4. Messen statt vermuten. Das Vorabladen des Hero-Bildes klang richtig und kostete fünf Punkte. Jede Annahme über Geschwindigkeit wird gemessen, vorher und nachher.
  5. Der Entwicklungsserver ist nicht das Hosting. Ohne Kompression und Cache-Kopfzeilen misst man 90 statt 96 – und optimiert an der falschen Stelle.