design-parity skill v1.4: Regressionen durch Redakteurs-Speicherungen

Neuer Abschnitt "When an editor's SAVE is the cause". Sobald Redakteure
an einer Seite arbeiten, taucht eine eigene Fehlerklasse auf: "es ist
kaputt und niemand hat etwas geaendert". Der Redakteur ist der Ausloeser,
nicht die Ursache — beim Speichern schreibt TYPO3 Teile des Datensatzes
neu, und was im Backend nicht ausdrueckbar war, wird wegnormalisiert.

Enthaelt die Diagnose-Reihenfolge (tstamp gegen Meldedatum, gespeicherten
Wert gegen Erwartung, erst dann Rendering) und die zwei bekannten
Mechanismen: CKEditor verwirft jede nicht im RTE-Preset deklarierte
Klasse, und ein FlexForm-select ohne leeren Eintrag speichert seinen
ERSTEN Eintrag — bei ja/nein also ueberall "ja". Dazu die Abhilfen,
inklusive Styling ueber Struktur statt Klassen, und die Query fuer die
Reichweite.

Zwei abgeleitete Regeln: nach jeder redaktionell sichtbaren Neuerung
fragen, was ein Speichern ohne Aenderung bewirkt; und auf "wo kann ich
das einstellen?" mit einem Ort antworten statt mit einer Person, also
einer deklarierten Site-Einstellung samt Pruefrezept.

Ausserdem: Bootstrap-Carousel-Autoplay laesst sich nicht ueber das
Intervall abschalten (Messwerte, Begruendung, data-bs-ride weglassen),
Hinweis auf die Wechselwirkung mit zufaelliger Slide-Reihenfolge, und
die Cache-Falle um ein komplett gecachtes Dokument erweitert.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-06 15:10:46 +02:00
co-authored by Claude Opus 5
parent b1042758fd
commit 871a3ffd19
+60 -2
View File
@@ -1,9 +1,9 @@
--- ---
name: typo3-t3bootstrap-live-design-parity 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), 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), 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)."
metadata: metadata:
author: Wappler author: Wappler
version: "1.3" version: "1.4"
--- ---
# TYPO3 v14 t3bootstrap — live-design parity # TYPO3 v14 t3bootstrap — live-design parity
@@ -43,6 +43,11 @@ placeholders below from the site you're matching; keep a per-project log of what
`fetch(cssUrl,{cache:'no-store'})` and grep the rule, or `grep` the compiled file on disk; don't `fetch(cssUrl,{cache:'no-store'})` and grep the rule, or `grep` the compiled file on disk; don't
trust the cached DOM. Same for server-rendered HTML — `fetch(url,{cache:'no-store'})` to confirm a trust the cached DOM. Same for server-rendered HTML — `fetch(url,{cache:'no-store'})` to confirm a
template change actually took, rather than the cached page. template change actually took, rather than the cached page.
- **⚠ The browser may also serve a whole cached DOCUMENT for a URL you believe is new.** Seen with a
persistent-profile browser: the DOM still showed the pre-change markup while `curl` on the same
path already returned the new one. Symptom: your fix "doesn't work" in the browser but is provably
live on the server. Cross-check with an in-page `fetch(location.pathname + '?x=' + Math.random(),
{cache:'no-store'})` and compare against the DOM before concluding anything about the fix.
2. With `browser_evaluate`, capture the real element's **computed style AND its explicit CSS rule** 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. (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. 3. Diff vs DDEV → decide **setting vs SCSS** (below) → apply → flush → re-screenshot → verify.
@@ -119,6 +124,49 @@ input, and turns a self-service setting into a support ticket.
--- ---
## When an editor's SAVE is the cause
Once a site is in the hands of editors, a whole class of "it broke and nobody changed anything" reports
appears. The editor is the **trigger, not the cause**: opening a record and pressing Save makes TYPO3
rewrite parts of it, and whatever was never expressible in the backend gets normalised away.
**Diagnose in this order** — it is much faster than reading CSS:
1. Compare the record's `tstamp` with the date the problem was first noticed. A match points at a save.
2. Diff the record's stored value against what you expect (a `pi_flexform` that grew from 6 to 165
fields, a `bodytext` that lost its classes). The size alone is usually the giveaway.
3. Only then look at rendering.
**Known mechanisms:**
- **CKEditor drops every class not declared in the RTE preset.** Hand-written classes in `bodytext`
survive only until someone opens that element and saves. No error, no warning. Check
`editor.config.style.definitions` in the preset YAML for the class; if it's absent, that's your cause.
**Fix:** declare the project's classes there (group them with a common name prefix so editors can tell
them from the framework's), and — where the markup allows it — derive the styling from **structure**
instead of classes (e.g. `:nth-child(odd|even)` for a strict number/label alternation), so the look
cannot be lost at all. Audit the blast radius before assuming it's one element:
`SELECT bodytext FROM tt_content WHERE deleted=0 AND bodytext LIKE '%class=%'` → extract the classes →
compare against the preset's list. Classes with no CSS anywhere are dead migration residue; ignore those.
- **A FlexForm `select` with no empty item stores its FIRST item on save.** If the options are just
"yes"/"no", every untouched field silently becomes **yes**. One save can therefore switch on dozens
of features nobody chose — including ones that break rendering outright. Symptom: a slider/gallery
that worked for months misbehaves right after an editorial edit, and its stored flexform is suddenly
huge. **Fix at the source:** give those selects an empty first item (a `selectSingleWithTypoScriptPlaceholder`
renderType is *designed* for that empty state — it then shows the TypoScript default as placeholder).
Then clean the affected records; the schema fix alone does not repair stored data.
- **Your own CSS outranking an editor-facing field** — see the spacing section above.
**Two rules that follow:**
- **After adding anything editor-facing, ask what happens when they press Save without touching it.**
That single question would have caught all three cases above.
- **Give the customer a place, not a person.** When the question is "where can I set this?", the good
answer is a declared site setting with a label and a category, visible in the backend — not a value
you maintain in a YAML they never see. Declare it in the sitepackage's
`Configuration/Sets/<Set>/settings.definitions.yaml` (`type`, `default`, `label`, `description`,
`category`), wire it into the template via `{$your.setting}`, and *verify it is registered*: a small
CLI bootstrap printing `$site->getSettings()->get('your.setting')` plus the definition from the set
registry proves both the value and the backend visibility.
---
## Layer 1 — Structural fixes (content that won't render). Do these FIRST. ## Layer 1 — Structural fixes (content that won't render). Do these FIRST.
These are v11→v14 migration artifacts, not design; they recur across many pages. These are v11→v14 migration artifacts, not design; they recur across many pages.
@@ -212,6 +260,16 @@ These are v11→v14 migration artifacts, not design; they recur across many page
edits don't show. `.carousel-fade` = crossfade vs `.slide` = slide. The `.carousel-indicators` strip edits don't show. `.carousel-fade` = crossfade vs `.slide` = slide. The `.carousel-indicators` strip
is a full-width high-z-index bar that steals hover from content beneath — reposition/shrink or is a full-width high-z-index bar that steals hover from content beneath — reposition/shrink or
`pointer-events:none`. Contain-vs-full-bleed per design. `pointer-events:none`. Contain-vs-full-bleed per design.
- **Switching autoplay OFF cannot be done via the interval.** Bootstrap 5's `_updateInterval()` reads
the interval from the **active slide**, not the container; slides normally carry none → `NaN`
it falls back to `defaultInterval`, and `setInterval(fn, false)` means **0 ms**. Measured on 5.3.8:
`data-bs-interval="false"` produced *3 slide changes in 2 seconds*. An empty interval is worse
still — that's the "interval type null" JS crash. **Omit `data-bs-ride` instead**: `_maybeEnableCycle()`
returns immediately, manual controls keep working (measured: 0 changes in 12 s). Keep the interval
value stored so re-enabling works.
- Beware the interaction with a **randomised slide order**: with autoplay off, a visitor who doesn't
click sees exactly one random slide out of N. Raise it before implementing — it may or may not be
what "manual only" was meant to achieve.
- **Translucent overlays — paint ONE shape:** a translucent box + a separate translucent shape beside - **Translucent overlays — paint ONE shape:** a translucent box + a separate translucent shape beside
it can never be seamless (hairline gap or doubled-alpha line). Merge into one element with one it can never be seamless (hairline gap or doubled-alpha line). Merge into one element with one
`clip-path` polygon + one `rgba()`. `clip-path` polygon + one `rgba()`.