design-parity skill v1.3: t3b_core spacing system

Add a dedicated section on where spacing is resolved (Spacing.yaml ->
per-site spacing.yaml -> tx_t3b_spacing) and why a spacing fix in the
custom SCSS partial silently disables the editor-facing field: the
utility class it emits is (0,1,0) and loses to any contextual rule.

Also covers the first/last-position leak into container columns, the
fact that the system has no notion of "has a background" (so seemingly
contradictory spacing complaints are two different cases), when SCSS is
still the right tool (per-column, not per-element), and how to verify
from the emitted markup instead of the rendering.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-06 13:30:53 +02:00
co-authored by Claude Opus 5
parent 59ce47e729
commit 674fbc9e46
+57 -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, 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), 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.2" version: "1.3"
--- ---
# TYPO3 v14 t3bootstrap — live-design parity # TYPO3 v14 t3bootstrap — live-design parity
@@ -64,6 +64,61 @@ placeholders below from the site you're matching; keep a per-project log of what
--- ---
## Spacing (margin/padding) — a THIRD place, and NEVER your SCSS partial
`t3b_core` ships an **editor-facing** spacing system. Writing a spacing fix into your custom SCSS
silently disables it for that element — this is the single easiest way to break the backend field
without any error appearing.
**Where spacing is resolved**, weakest → strongest:
1. `EXT:t3b_core/Configuration/Yaml/Spacing.yaml` — the steps (`none/xs/s/m/l/xl/xxl`, rem values)
and global defaults (typically `padding_top`/`padding_bottom` = `l`).
2. `config/sites/<SITE_ID>/spacing.yaml` — per site, itself a cascade:
`defaults:``first:` / `last:` (position in the column) → `ctypes.<CType>:`
`containers.<containerCType>:` (each of the latter two may carry its own `defaults/first/last`).
Field keys: `padding_{top,right,bottom,left}`, `margin_{top,bottom}`. Values: a step key, or a
per-breakpoint map. There is also a backend module that writes this file.
3. `tt_content.tx_t3b_spacing` — per element, TCA `type: json`, rendered as the "Spacing/Abstände"
box-model palette: `{"padding_top":"none"}` or `{"padding_top":{"xs":"none","md":"l"}}`.
All of it ends up as `t3b-{p,m}{t,r,b,l}-<step>` utility classes on the frame element.
- **THE TRAP.** A rule such as
`.page-content > .frame-type-header:first-child + .frame { padding-top: 0 }` has specificity
(0,4,0) and beats the utility class `.t3b-pt-l` (0,1,0). The editor then picks a spacing step and
**nothing happens** — no warning, the field simply appears broken. Confirm by simulating an editor:
swap the class in the browser to the strongest step and read the computed value; if it stays put,
a CSS rule is winning. → Express spacing in `spacing.yaml` (defaults) or `tx_t3b_spacing`
(exceptions), never in the SCSS partial.
- **Positional rules leak into containers.** `first`/`last` is computed per
`(pid, colPos, language, tx_container_parent)`, so a `ctypes.<X>.first` rule ALSO matches elements
that are first inside a *container column*. Measure the blast radius before writing the rule:
`SELECT parent.CType, COUNT(*) FROM tt_content c JOIN tt_content parent ON parent.uid=c.tx_container_parent
WHERE c.CType='<X>' AND c.deleted=0 AND c.tx_container_parent>0 GROUP BY parent.CType;`
Neutralise with `containers.<containerCType>.first:` re-asserting the extension defaults (harmless
for other CTypes, since it only restates what they already inherit).
- **The system has no notion of "has a background".** The same frame padding is dead whitespace on a
plain element but becomes the *inner padding of a band* once the element carries a `bg-*` class —
so one positional default cannot serve both, and requirements like "less gap under the page title"
and "more gap inside the grey box" only *look* contradictory. Keep the default, and set the
exception per element (`{"padding_top":"none"}`) — it stays visible and editable in the backend.
Find the elements needing it (first element after a page-title header, no band class):
`… WHERE c1.CType='header' AND c1.colPos=0 AND c2.element_classes NOT LIKE '%bg-%' …` — do this
before estimating effort, the count is usually far smaller than it looks.
- **When SCSS IS still correct:** the spacing system works per content ELEMENT, never per column. A
two-column element where one column must stay flush (a background-media image) while the other
needs an inset can only be solved in CSS — target the inner column (e.g. `.ce-bodytext`), not the
frame, so you are not overriding anything the editor could have set.
- **Verify from the emitted markup, not the rendering:**
`curl -s "<url>?cb=$RANDOM" | grep -o 't3b-pt-[a-z]*'`. `spacing.yaml` is read at render time and
the result is page-cached → `cache:flush` after editing it, and mind the browser's cached
`main.css` (see the cache section).
**General principle this is an instance of:** when a theme offers an editor-facing control for
something, configure *that* control; a CSS override of it is invisible to the editor, outranks their
input, and turns a self-service setting into a support ticket.
---
## 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.