Sechs Erkenntnisse aus einem Ticketdurchlauf einarbeiten

Upgrade-Skill 1.3 - neuer Step 9b: die uebrig gebliebenen sys_template-
Datensaetze loeschen. "Via site sets, not sys_template" liest sich wie "die
Tabelle ist erledigt", aber v14 wertet sie weiter aus (TCA in cms-frontend,
PageInformationFactory ruft getSysTemplateRowsByRootline). Ein Alt-Template
mit clear=3 OBERHALB des Site-Roots loescht Constants und Setup, also auch
das Set-TypoScript - und zwar nur fuer Code, der das FE-TypoScript im
Backend mit eigener Rootline neu aufbaut, weil der Core die Rootline am Site
abschneidet. Symptome sehen unverwandt aus (fehlende FormEngine-Platzhalter,
leere Template-Layout-Listen, nicht erscheinende FlexForm-Sheets), Kennzeichen
ist: Frontend richtig, Backend falsch. Mit den drei Pruefungen, die das
Loeschen vorher absichern, und Soft-Delete statt DELETE.

Design-Parity 1.9 - drei Punkte:
- Vergleichsschleife: um zu belegen, dass ein Eingriff das Frontend NICHT
  veraendert, muss vor dem Diff das Render-Rauschen weggefiltert werden
  (picture-/img-Hashes, CSS-mtime), sonst weicht jede Seite ab und der Beweis
  faellt aus.
- Neuer Abschnitt zum Backend-Formular mit Playwright: es steckt in einem
  iframe (und der naheliegende Frame-Filter erwischt den Hauptframe mit),
  innerText ist in inaktiven Tabs LEER, auf den eigenen field-item eingrenzen,
  Tab-Aktivierung ist unzuverlaessig, den SAVE testen - und die UI-Reichweite
  einer Aenderung an einer FormEngine-Renderbedingung vorher/nachher zaehlen
  (aus 9 Schaltern wurden unbemerkt 83).
- Settings: Booleans in settings.yaml gehoeren in Anfuehrungszeichen. YAML
  true wird zum Constant "1", YAML false zu einem LEEREN Constant; das
  Frontend ueberlebt es, der Backend-Platzhalter nicht.

Deploy-Skill 1.3 - die dort empfohlene Form MYSQL_PWD="$(cat …)" wird fuer
Schreibzugriffe regelmaessig blockiert. Durchgegangen ist ein Skript, das das
Passwort selbst aus DBPASSFILE liest, sodass nur ein Pfad in der
Kommandozeile steht. Dazu: solche Skripte gezielt und idempotent auf dem
aktuell gespeicherten Stand arbeiten lassen statt ein vorbereitetes Blob
zurueckzuschreiben, sonst ueberfaehrt man den parallelen Save eines
Redakteurs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-10 13:14:02 +02:00
co-authored by Claude Opus 5
parent 38b9ae49e5
commit 00b3a1a15a
3 changed files with 103 additions and 5 deletions
+51 -2
View File
@@ -1,9 +1,9 @@
---
name: typo3-t3bootstrap-live-design-parity
description: "Match a TYPO3 v14 t3bootstrap-based build (local DDEV) to a reference LIVE site page by page — the frontend/design phase after a v11->v14 upgrade. Use when making an upgraded TYPO3 site look like its old/live counterpart, when doing per-page visual parity against a reference URL, when content that exists in the DB isn't rendering after a v11->v14 migration, or when customizing a t3bootstrap sitepackage's templates/SCSS/settings. Covers: the Playwright compare loop (computed style + CSS rule), the settings-vs-SCSS decision, the t3b_core spacing system (spacing.yaml / tx_t3b_spacing) and why spacing must never be fixed in SCSS, recurring structural fixes (stranded colPos, missing templateLayout partials, template partialRootPaths precedence/shadowing, on-demand component CSS, unregistered CTypes), a full visual-parity checklist (layout/width, typography, colors, links/buttons, equal-sizing, icons, borders, footer, header/nav, hero carousel incl. why Bootstrap autoplay cannot be switched off via the interval), diagnosing regressions caused by an editor's SAVE (CKEditor dropping undeclared classes, FlexForm selects with no empty item flipping to "yes"), and cache-safe techniques (full-bleed bands, image-border restyle, responsive crop variants, client-side randomisation, FAL-via-DataHandler). Assumes the site already boots on v14 (see the v11->v14 upgrade skill)."
description: "Match a TYPO3 v14 t3bootstrap-based build (local DDEV) to a reference LIVE site page by page — the frontend/design phase after a v11->v14 upgrade. Use when making an upgraded TYPO3 site look like its old/live counterpart, when doing per-page visual parity against a reference URL, when content that exists in the DB isn't rendering after a v11->v14 migration, or when customizing a t3bootstrap sitepackage's templates/SCSS/settings. Covers: the Playwright compare loop (computed style + CSS rule, and how to prove a change did NOT alter the frontend), driving the BACKEND record form/FormEngine with Playwright (it lives in an iframe; innerText is empty in inactive tabs), the settings-vs-SCSS decision incl. why settings.yaml booleans must be quoted strings, the t3b_core spacing system (spacing.yaml / tx_t3b_spacing) and why spacing must never be fixed in SCSS, recurring structural fixes (stranded colPos, missing templateLayout partials, template partialRootPaths precedence/shadowing, on-demand component CSS, unregistered CTypes), a full visual-parity checklist (layout/width, typography, colors, links/buttons, equal-sizing, icons, borders, footer, header/nav, hero carousel incl. why Bootstrap autoplay cannot be switched off via the interval), diagnosing regressions caused by an editor's SAVE (CKEditor dropping undeclared classes, FlexForm selects with no empty item flipping to "yes"), and cache-safe techniques (full-bleed bands, image-border restyle, responsive crop variants, client-side randomisation, FAL-via-DataHandler). Assumes the site already boots on v14 (see the v11->v14 upgrade skill)."
metadata:
author: Wappler
version: "1.8"
version: "1.9"
---
# TYPO3 v14 t3bootstrap — live-design parity
@@ -77,10 +77,50 @@ placeholders below from the site you're matching; keep a per-project log of what
it decides whether anything you measure afterwards means something. `curl` is NOT a substitute
behind HTTP basic auth (staging) — it returns 401 while the browser holds the credentials.
Test behaviour only after that fetch and the DOM agree.
- **To prove a change did NOT alter the frontend, diff the HTML — but normalise the per-render noise
first.** TYPO3 emits values that differ on every render, so a raw `diff` reports every page as
changed and the proof evaporates: `id="picture-<hash>"` / `id="img-<hash>"` on responsive images
(regenerated per render), and the `?<mtime>` on `main.css`/`fonts.css` after any `cache:flush`
recompile. Filter those, then diff:
```
norm() { sed 's|/typo3temp/assets/css/[^"]*|CSS|g; s|picture-[0-9a-f]*|P|g; s|img-[0-9a-f]*|I|g'; }
diff <(norm < before.html) <(norm < after.html) && echo "identisch"
```
Capture the "before" over **several** pages (a content page, the home page, one with news teasers)
before touching anything — after the change it is too late. This is the evidence that turns "I
think nothing broke" into "4 pages byte-identical".
2. With `browser_evaluate`, capture the real element's **computed style AND its explicit CSS rule**
(getComputedStyle + walk `document.styleSheets` for the selector). Don't assume inheritance.
3. Diff vs DDEV → decide **setting vs SCSS** (below) → apply → flush → re-screenshot → verify.
## Driving the BACKEND with Playwright (FormEngine)
Sooner or later the question is "what does the editor actually see in this field?" — the record form is
a different beast from the frontend, and four things silently produce wrong measurements. Each of these
cost a wasted measurement round:
- **⚠ The record form lives in an `iframe`.** `document.querySelectorAll(...)` on the main document
returns **0 fields** and looks like an empty form. Use `page.frames()`. And beware the obvious
selector: `page.frames().find(f => f.url().includes('record/edit'))` matches the **main** frame too,
because the outer URL contains that path — pick `f => f !== page.mainFrame()` instead.
- **⚠ `innerText` returns EMPTY for anything in an inactive FormEngine tab.** FlexForm sheets are tabs,
so every field outside the active one reads as blank — which looks exactly like "no label / no hint
rendered". Use **`textContent`**, which ignores visibility. (`offsetParent === null` likewise only
means "its tab isn't open", not "hidden".)
- **Scope to the field's own `.t3js-formengine-field-item`.** Walking up to a parent to find some text
pulls in the neighbouring fields' hints and produces confident nonsense (a hint from the field *above*
attributed to this one).
- **Activating a tab is unreliable.** `getByRole('tab', {name})` timed out and a JS `.click()` on
`[role="tab"]` did nothing on v14. Don't fight it: read via `textContent` regardless of the active
tab. Buttons work normally — `button[name="_savedok"]` is Save.
- **Test the SAVE, not just the render**, whenever the concern is "does the stored configuration
survive?" Press Save in the real form, then check the DB and the frontend. That is the only test that
reproduces what an editor does, and it is where FlexForm bugs actually surface.
- **⚠ Count the affected widgets BEFORE and AFTER changing a FormEngine element's render condition.**
These blocks often render more than they appear to — a placeholder block can also carry the nullable
"Set element specific value" toggle, so a change to *when* it renders silently adds or removes that
control. A one-line fix of mine turned 9 toggles into **83** (and the naive variant would have removed
all 9); only counting `.t3js-form-field-eval-null-placeholder-checkbox` in the real form before and
after revealed it. Then choose the variant that keeps the previous footprint.
## When the report is a rendering artefact, not a defect
Some "it looks broken" reports are sub-pixel rasterisation: a hairline that washes out, a circle whose
cap renders as a straight edge, an outline that vanishes. They are **display-dependent**, which is why
@@ -106,6 +146,15 @@ one person sees them and another does not, and why they come and go with the zoo
a bug fix. Do not ship a change that your own numbers say is not an improvement.
## Settings vs SCSS decision
- **⚠ Write booleans in `settings.yaml` as QUOTED STRINGS, not YAML booleans.** Site settings arrive in
TypoScript as constants, and the conversion is lossy: YAML `true` becomes the constant **`"1"`** and
YAML `false` becomes an **EMPTY** constant. The frontend usually survives that (a falsy check, or an
extension mapping `'true'`/`'false'` to real booleans), so it looks fine and the bug hides in the
backend: an option field whose items are the strings `true`/`false` can no longer match the constant,
so its "Default: …" hint shows `Default: "1"` — or, for the empty case, matches the FIRST item and
claims `Default: "Default"`. With `'true'` / `'false'` the mapping works and the hint reads
`(Default: "Yes"/"No")`, with identical frontend output. Applies to any `plugin.tx_*` value you set
through `settings.yaml`, not just declared theme settings.
- **Setting** (`config/sites/<SITE_ID>/settings.yaml`) for anything mapped in the theme's
`Configuration/TypoScript/Plugin/Setup/tx_wsscss.typoscript`: `design.color.{primary,secondary,
body-color,headings-color,header-bg,page-bg,footer-bg,footer-color,footer-headline-color,…}`,