Verallgemeinerte Erkenntnisse aus einem Ticketdurchlauf einarbeiten
Design-Paritaet 1.5 -> 1.6: - Site-Set-page.tsconfig erreicht keine Datensaetze in Ordnern neben dem Site-Root; solche Konfiguration gehoert in die globale page.tsconfig der Extension. Symptom ist, dass eine TCEFORM-Aenderung genau fuer einen Datensatztyp wirkungslos bleibt. - Angebotene und gerenderte Crop-Variante koennen auseinanderlaufen, dann beschneidet die Redaktion ins Leere. - Feste Bildhoehe laesst das Seitenverhaeltnis ueber die Breakpoints wandern, sodass kein Zuschnitt mehr ueberall passt; aspect-ratio statt fester Hoehe. - Umbrechende Labels brauchen eine reservierte Zeilenhoehe, sonst springt der Rhythmus des Rasters; dabei die Untermarge des Absatzes neutralisieren und die schmalste Geraetebreite pruefen, nicht nur 360. - :has() laesst sich nicht in :has() verschachteln, die Regel wird komplett verworfen - ohne jede Fehlermeldung. - Neuer Abschnitt zu Meldungen, die Rasterartefakte statt Fehler sind: gegen die Referenzseite pruefen, ueber mehrere Zoomstufen UND Subpixel-Versaetze messen, das Ganze per Canvas in einem Durchgang - und rechtzeitig sagen, dass Geometrie es nicht loest. v11->v14 1.1 -> 1.2: - Ein "verlorenes" Feld ist oft ein umgebautes Feld; zwei Spalten werden gern zu einem Feld plus Schalter. Erst das neue Modell pruefen, bevor man eine fremde Extension patcht - dann ist es eine Datenmigration. Dazu die abgesicherte UPDATE-Bedingung und der Hinweis, dass Fluid keinen !-Operator kennt. - Vor "Regression" gegen die Referenzseite pruefen: zwei Tickets zum selben fehlenden Element koennen Verlust und Neuwunsch sein. Cutover 1.1 -> 1.2: - Eingebettete Fremddienste sperren sich per frame-ancestors oft gegen Staging. Solche Tests sind dort in keinem Browser moeglich; frueh klaeren, den eigenen Anteil trotzdem pruefen. Redmine 1.3 -> 1.4: - Textile beendet den Durchstrich am ersten Bindestrich im Text, ein Pfeil laesst also die Handlungsaufforderung ungestrichen stehen; HTML hilft nicht, Redmine escapt es. Und das Ergebnis nachsehen, die API bestaetigt nur die Speicherung. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@ 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)."
|
||||
metadata:
|
||||
author: Wappler
|
||||
version: "1.5"
|
||||
version: "1.6"
|
||||
---
|
||||
|
||||
# TYPO3 v14 t3bootstrap — live-design parity
|
||||
@@ -66,6 +66,30 @@ placeholders below from the site you're matching; keep a per-project log of what
|
||||
(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.
|
||||
|
||||
## 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
|
||||
one person sees them and another does not, and why they come and go with the zoom level.
|
||||
|
||||
- **First ask whether the reference site does the same.** If live emits the identical asset at the
|
||||
identical size, it is not a regression — say so before spending a day on it.
|
||||
- **A single zoom level proves nothing.** An element's box lands on a fractional device-pixel position
|
||||
that depends on zoom AND on where it sits in the page. A change that looks like a fix at 100 % can
|
||||
be measurably worse at 120 %. Always sweep several zoom levels **and** several sub-pixel offsets.
|
||||
- **Measure the whole sweep in ONE call instead of a screenshot per variant.** Build each variant as an
|
||||
SVG data-URL, `drawImage` it into a canvas at the exact device size and offset, and read the edge
|
||||
rows with `getImageData`. Same rasteriser as the page, no screenshot round-trips:
|
||||
```js
|
||||
ctx.drawImage(img, 0, offset, size, size); // size = base * zoom, offset = 0 … 0.8
|
||||
const d = ctx.getImageData(0, 0, n, n).data; // then count alpha>128 per row
|
||||
```
|
||||
Compare the top and bottom edge rows; a large difference is the asymmetry the user is seeing.
|
||||
- **Know when to stop and say so.** Geometry often cannot fix it: shrinking a radius just moves the
|
||||
bad zoom levels, thickening an inner ring changes nothing because the OUTER edge does the
|
||||
rasterising, and making the icon bigger scales the artefact with it. Report the measurements and let
|
||||
the customer decide between living with it and redrawing the asset — that is a design decision, not
|
||||
a bug fix. Do not ship a change that your own numbers say is not an improvement.
|
||||
|
||||
## Settings vs SCSS decision
|
||||
- **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,
|
||||
@@ -184,6 +208,23 @@ rewrite parts of it, and whatever was never expressible in the backend gets norm
|
||||
## 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.
|
||||
|
||||
- **⚠ Site-set page TSconfig does NOT reach records in storage folders beside the site root.** A site
|
||||
set's `Configuration/Sets/<Set>/page.tsconfig` only applies to pages in that site's ROOTLINE.
|
||||
Storage folders for news, addresses, jobs are frequently SIBLINGS of the site root under a common
|
||||
parent, not descendants — so TCEFORM you put in the set never reaches the editing form of those
|
||||
records, while it visibly works on normal pages. Symptom: your TCA/TCEFORM change "does nothing"
|
||||
for exactly one record type. **Check the storage folder's pid, not a content page:**
|
||||
`BackendUtility::getPagesTSconfig(<storagePid>)` in a CLI bootstrap, and compare with a site page.
|
||||
**Fix:** move it to the extension's **global** `Configuration/page.tsconfig`, which TYPO3 loads for
|
||||
every page. Load order then decides who wins — your sitepackage must load AFTER the extension you
|
||||
are overriding (`vendor/bin/typo3 extension:list` to confirm), which it normally does.
|
||||
- **The crop variant OFFERED to the editor may not be the one RENDERED.** Extensions ship global
|
||||
page TSconfig that **replaces** the crop variants of a field with the single ratio their own layout
|
||||
wants. If your templates request a different variant, an editor can crop all day with no effect and
|
||||
the image is emitted uncropped. Trace BOTH ends before touching anything: which variant the template
|
||||
asks for (often a setting such as `settings.imageFormat`, with a fallback for the literal value
|
||||
`default`), and which variants the form offers at that record's pid. They must be the same name.
|
||||
|
||||
- **Stranded `colPos=1` (the #1 recurring bug).** v11 content sitting at `colPos 1` (or 2/3) is not
|
||||
rendered by the v14 backend layout → the page looks half-empty though the content is in the DB.
|
||||
Find it site-wide:
|
||||
@@ -239,6 +280,24 @@ These are v11→v14 migration artifacts, not design; they recur across many page
|
||||
- **Repeated items (cards/teasers/logos/icon tiles):** live usually forces **equal height AND width**
|
||||
across the row (`height:100%` in an equal-height row; images `object-fit:cover` + fixed height;
|
||||
logo boxes equal size). DDEV often renders ragged — measure siblings, don't assume.
|
||||
- **⚠ A FIXED HEIGHT on a cropped image box drifts in aspect ratio across breakpoints.** The height
|
||||
stays put while the column width grows, so the same box is e.g. 1.58 on desktop and 1.82 on a
|
||||
phone — and then **no single crop variant can fit both**, which reads as "the crop is off" on one
|
||||
device. Use `aspect-ratio: <w> / <h>; height: auto` instead so the box keeps ONE ratio everywhere,
|
||||
then one crop variant is enough. Measure live's box before choosing the ratio; it usually already
|
||||
holds a constant one.
|
||||
- **⚠ Wrapped labels under tiles: reserve the line box, or the grid rhythm breaks.** In a wrapping
|
||||
Bootstrap row each flex line sizes itself from its tallest item, so one two-line label makes that
|
||||
whole row taller and the icon-to-icon pitch varies. Give the label `min-height: <maxLines> ×
|
||||
line-height` **and zero the label `<p>`'s bottom margin** — otherwise that margin is added on
|
||||
exactly the tiles that actually reach the maximum, which are the ones you were trying to level.
|
||||
Derive `maxLines` per breakpoint by measuring, and **check the narrowest device (320px), not just
|
||||
360** — the extra column narrowness there routinely needs one line more.
|
||||
- **⚠ `:has()` cannot be nested inside `:has()` — the whole rule is silently discarded.** A selector
|
||||
like `.a:has(.x):has(+ .a:has(.x))` is invalid CSS, so the browser drops it entirely and you get
|
||||
no error anywhere. Very easy to write when chaining "frame that is followed by another frame"
|
||||
rules. If a rule seems to have no effect, check the computed value on the element first; a
|
||||
property that never appears at all points at an invalid selector, not at specificity.
|
||||
- **background-media hero image** (textmedia with `asBackground`, usually empty text — e.g. section
|
||||
landing/"Auftraggeber" pages): the image is a CSS background on a grid column sitting on a grey band.
|
||||
Live renders it **flush + container-width**; DDEV insets it (frame `t3b-pt-l/pb-l` padding + a
|
||||
|
||||
Reference in New Issue
Block a user