commit af9368177b00531d5bf269332cf70c9c645ec9ed Author: Sven Wappler Date: Mon Jun 15 09:58:33 2026 +0200 Initial t3bootstrap-skills plugin: three skills for T3Bootstrap on TYPO3 v14 - t3bootstrap-site-package: composer + site sets + when to add a local extension - t3bootstrap-overrides: 4-layer override surface (Settings → SCSS → Fluid → DataProcessor) - t3bootstrap-content-elements: 23 t3bs_* CE catalog + migration matrix (HTML/WP/Joomla/older TYPO3) + Flux-bs5 upgrade wizard Mirrors the structure of wapplersystems/typo3-skills (.claude-plugin/{plugin.json,marketplace.json}, .agents/skills//{SKILL.md,agents/openai.yaml}). Co-Authored-By: Claude Opus 4.7 (1M context) diff --git a/.agents/skills/t3bootstrap-content-elements/SKILL.md b/.agents/skills/t3bootstrap-content-elements/SKILL.md new file mode 100644 index 0000000..5cf3ce3 --- /dev/null +++ b/.agents/skills/t3bootstrap-content-elements/SKILL.md @@ -0,0 +1,417 @@ +--- +name: t3bootstrap-content-elements +description: Choose the right content element when migrating content into a T3Bootstrap-based TYPO3 v14 site. Covers the full catalog of 23 `t3bs_*` container-based CEs from `t3bootstrap/container-bs5-templates`, the custom CEs from `t3bootstrap/core` (Card, CompareSlider, Countdown, Counterbar, NumberCarousel) and `t3bootstrap/hero-item`, plus a decision matrix for source materials — static HTML/Bootstrap markup, WordPress blocks, Joomla modules, older TYPO3 sites with `flux_bs5_templates` / `gridelements` / `dce` / `bootstrap_package`. Includes the parent-child container nesting rules, the `t3bsContainerBs5MigrateFluxBs5` upgrade wizard and how to run it, the colPos slot conventions, and guidance on when to pick a core CE (`textmedia`, `image`, `header`, `bullets`) over a custom one. Use this skill whenever migrating content TO a T3Bootstrap project, picking which CE to use for a given visual pattern, debugging "child element doesn't appear in parent" issues, or planning a Flux-to-Container content rewrite. +--- + +# T3Bootstrap Content Elements + +Use this skill to decide **which content element** to use when bringing content into a +T3Bootstrap-based TYPO3 v14 project — from static HTML, another CMS, an older TYPO3 +version, or hand-rebuilt by an editor. + +> **Companion skills:** +> - [`t3bootstrap-site-package`](../t3bootstrap-site-package/SKILL.md) — make sure the right packages and Site Sets are present before placing CEs. +> - [`t3bootstrap-overrides`](../t3bootstrap-overrides/SKILL.md) — when a stock CE almost fits but you need to tweak its template. + +## The Three CE Layers + +A T3Bootstrap project draws content elements from **three sources**, in order of +specificity: + +1. **Core CEs** from TYPO3's `fluid_styled_content` — `textmedia`, `image`, `header`, `bullets`, `table`, `uploads`, `menu_*`, `shortcut`, … + T3Bootstrap **rewraps** these via `t3bootstrap/core` (extended `tt_content` fields, Bootstrap-aware Fluid templates). +2. **Custom CEs from `t3bootstrap/core`** — `t3bs_card`, `t3bs_compareslider`, `t3bs_countdown`, `t3bs_counterbar`, `t3bs_numbercarousel`. These are **non-container**, single-element CEs with a fixed shape. +3. **Container CEs from `t3bootstrap/container-bs5-templates`** — the 23 `t3bs_*` elements that wrap or compose children via `b13/container`. + +**Rule of thumb when migrating:** + +- *Plain prose with optional images* → core `textmedia`. Don't reach for a container. +- *Headline + paragraph + image + button, arranged together* → still likely `textmedia` + `t3bs_buttonlink`, **unless** the visual requires Bootstrap card styling. +- *Multiple of these arranged in a Bootstrap-styled box* → `t3bs_card` (single) or `t3bs_cards` (grid). +- *Anything that **wraps** other content elements* → a container (`t3bs_container`, `t3bs_fluidrow` + `t3bs_column`, …). + +## Core CEs — Use These First + +The core CEs cover most static content. T3Bootstrap doesn't replace them; it gives them +better wrappers, frame classes, breakpoint grids and additional fields (`element_classes`, +`image_classes`, `imagecols_grid`, `background_media`). + +| Core CType | When to use | +| --- | --- | +| `textmedia` | Default body content with optional images. Migrating any "paragraph + image" combination. | +| `text` | Pure prose, no media. | +| `header` | Standalone headline above a section. | +| `image` | Image only, optional caption. Migrating image galleries → one `image` per picture. | +| `textpic` | Legacy form of textmedia kept for backwards compatibility — prefer `textmedia` for new content. | +| `bullets` | Lists. | +| `table` | Tabular data. | +| `uploads` | File downloads. | +| `menu_*` | Sitemap, categorized pages, pages, recently updated, related pages. | +| `shortcut` | Reference another tt_content record (useful for "include this CE on multiple pages"). | +| `html` | Raw HTML. **Migration source for everything you can't classify yet** — but a follow-up pass should re-classify into proper CEs. | + +T3Bootstrap-extended fields available on every CE: + +- `element_classes` — extra CSS classes on the CE wrapper. +- `image_classes` — extra classes on image renderings. +- `imagecols_grid` — JSON breakpoint grid for image columns (responsive 1/2/3/4-col layouts). +- `background_media` — FAL reference for a section background image/video. +- Spacing palette (provided by `t3bootstrap/core`) — responsive padding/margin per breakpoint, stored as JSON in dedicated columns. *(See the spacing-stepper concept memo if extending this.)* + +## Custom Single CEs from `t3bootstrap/core` + +These are not containers — they're standalone elements with a fixed shape. Each is enabled +by including the matching Site Set. + +| CType | Site Set | Source | Use for | +| --- | --- | --- | --- | +| `t3bsCard` (custom card) | `t3bootstrap/card-element` | `vendor/t3bootstrap/core/Configuration/TCA/Overrides/600_content_element_card.php` | A simple card without nested CEs — image, title, text, link. *Use over `t3bs_card` when no nested content elements are needed.* | +| `t3bsCompareSlider` | `t3bootstrap/compareslider-element` | `600_content_element_compare_slider.php` | Before/after image comparison with a draggable divider. | +| `t3bsCountdown` | `t3bootstrap/countdown-element` | `600_content_element_countdown.php` | Date/time countdown widget. | +| `t3bsCounterbar` | `t3bootstrap/counterbar-element` | `202_content_element_counterbar.php` | Animated number counter ("12,000+ customers"). | +| Hero | `t3bootstrap/hero-item` | `vendor/t3bootstrap/hero-item/Configuration/TCA/Overrides/tt_content.php` | Full-width banner with title, subtitle, background image/video, CTA. *Use over `t3bs_container` for landing pages.* | +| Teaser | `wapplersystems/teaser` | n/a | Image + headline + text teaser block, links to a page. | +| Testimonial | `wapplersystems/testimonials` | n/a | Quote + author + photo. | +| Slider | `wapplersystems/ws-slider*` | n/a | Each variant (`flexslider`, `swiper`, `tinyslider`, `slick`, `bootstrap`) is one CE. *Use over `t3bs_carousel` when image-only with no inner content.* | + +## Container CEs — The 23 `t3bs_*` Elements + +Defined in `vendor/t3bootstrap/container-bs5-templates/Configuration/TCA/Overrides/tt_content.php` +and registered via `b13/container`. **All container slots use the colPos range `21000–21079`**. + +### Layout containers — wrap arbitrary content + +| CType | colPos | Children allowed | Purpose | +| --- | --- | --- | --- | +| `t3bs_container` | `21001` | any | Bootstrap `.container` / `.container-sm` / `.container-fluid` wrapper. Set `tx_t3bs_container_type` to choose the breakpoint. | +| `t3bs_fluidrow` | `21041` | only `t3bs_column` | Bootstrap `.row` with per-breakpoint column grid. | +| `t3bs_column` | `21050` | any | A `.col-*` cell. Width per breakpoint configured via TCA. Always a child of `t3bs_fluidrow`. | + +### Content containers — wrap CEs in styled boxes + +| CType | colPos | Purpose | +| --- | --- | --- | +| `t3bs_card` | `21008` | Bootstrap card with header/image/body/footer regions; children render in the body. | +| `t3bs_cards` | `21009` | A **set** of cards rendered as group / deck / grid / columns layout (`tx_t3bs_cards_layout`). Children: any. | +| `t3bs_alert` | `21003` | Bootstrap `.alert` (primary/success/danger/warning/info/light/dark) with optional close button. | +| `t3bs_panel` | `21005` | Styled panel (legacy Bootstrap-3-style box). | +| `t3bs_well` | `21004` | Inset well (small/normal/large via `tx_t3bs_well_style`). | +| `t3bs_example` | `21002` | Code-example container (renders the children inside a `.example` box used for documentation pages). | +| `t3bs_thumbnail` | `21006` | Image thumbnail with caption. | +| `t3bs_media` | `21007` | Bootstrap media object — image flush left or right of text. | + +### Interactive containers — children become panels/slides/items + +| CType | child colPos | Allowed children | Purpose | +| --- | --- | --- | --- | +| `t3bs_accordion` | `21011` | `t3bs_accordion_item` only | Accordion. Each `t3bs_accordion_item` becomes one collapsible panel. | +| `t3bs_accordion_item` | `21019` | any | One accordion panel — contains the body content. | +| `t3bs_tabs` | `21021` | `t3bs_tab_item` only | Tab container (tab/pill via `tx_t3bs_tabs_mode`). | +| `t3bs_tab_item` | `21029` | any | One tab pane. | +| `t3bs_carousel` | `21031` | `t3bs_carousel_item` only | Image/content carousel. Controls/indicators/interval configurable. | +| `t3bs_carousel_item` | `21039` | any | One slide. | +| `t3bs_timeline` | `21071` | `t3bs_timeline_item` only | Vertical timeline. | +| `t3bs_timeline_item` | `21079` | any | One timeline entry. | + +### Navigation containers — buttons and megamenu + +| CType | child colPos | Children | Purpose | +| --- | --- | --- | --- | +| `t3bs_buttongroup` | `21010` | `t3bs_buttonlink` only | Group of buttons (Bootstrap button group). | +| `t3bs_buttonlink` | — | (standalone) | Single button. Standalone CE — can also appear outside a button group. | +| `t3bs_megamenu` | `21061` | `t3bs_megamenu_item` only | Megamenu dropdown. | +| `t3bs_megamenu_item` | `21069` | any | One megamenu column / sub-section. | + +### Parent-child Rules — Common Pitfalls + +| Symptom | Cause | Fix | +| --- | --- | --- | +| `t3bs_column` placed without a parent → renders standalone | `t3bs_column` has no UI restriction at root but is only meaningful inside `t3bs_fluidrow`. | Always insert via "create child" inside a `t3bs_fluidrow`. | +| `t3bs_accordion_item` rendered outside an accordion | Same — no rendering wrapper. | Same — insert as a child of `t3bs_accordion`. | +| Editor can't find `t3bs_carousel_item` in the CE wizard root | Correct — these CTypes are deliberately filtered. | Edit by entering the parent's child slot. | +| Carousel/Tabs/Accordion show "0 children" but you added textmedia | You inserted into the parent's *own* colPos (e.g. 0) instead of the parent's *child slot colPos*. | Move the children to the parent's child slot (e.g. `colPos = 21011` for an accordion). | + +## Migration Decision Matrix + +For each source pattern, the recommended T3Bootstrap target. + +### From static HTML / Bootstrap pages + +| Source markup | Target CE | +| --- | --- | +| `
` with `.container` and paragraphs | `t3bs_container` wrapping `textmedia` children, **or** just `textmedia` with `imagecols_grid` if it's a single paragraph block | +| `.row > .col-md-4` × N | `t3bs_fluidrow` with N `t3bs_column` children. Set column widths per breakpoint via TCA. | +| `.card` block (image + title + body) | `t3bs_card` (with child content for the body) **or** the simpler standalone `t3bsCard` (no children needed) | +| `.card-deck` / `.row-cols-md-3 > .col` of cards | `t3bs_cards` with `tx_t3bs_cards_layout = deck` (or `grid-cards`) | +| `.accordion` / `
` | `t3bs_accordion` + one `t3bs_accordion_item` per item | +| Bootstrap `.nav-tabs` / `.nav-pills` | `t3bs_tabs` + `t3bs_tab_item` per tab | +| Bootstrap `.carousel` | `t3bs_carousel` + `t3bs_carousel_item` per slide | +| `.alert.alert-warning` | `t3bs_alert` with `tx_t3bs_alert_class = warning` | +| `.btn.btn-primary` | `t3bs_buttonlink` (with optional `t3bs_buttongroup` parent) | +| Hero `
` with full-bleed background | `Hero` (from `t3bootstrap/hero-item`) | +| Vertical timeline | `t3bs_timeline` + `t3bs_timeline_item` per step | +| Before/after slider | `t3bsCompareSlider` | +| Animated counter | `t3bsCounterbar` | +| Countdown timer | `t3bsCountdown` | +| Image gallery | `wapplersystems/filecollection-gallery` set with a `file_collection` CE, **or** N `image` CEs in a `t3bs_fluidrow`. | + +### From WordPress + +| Gutenberg block | Target | +| --- | --- | +| `core/paragraph` + `core/image` | `textmedia` | +| `core/heading` | `header` | +| `core/list` | `bullets` | +| `core/table` | `table` | +| `core/columns` + `core/column` | `t3bs_fluidrow` + `t3bs_column` | +| `core/group` (background, padding) | `t3bs_container` | +| `core/cover` (full-width image + heading) | `Hero` (from `t3bootstrap/hero-item`) | +| `core/buttons` + `core/button` | `t3bs_buttongroup` + `t3bs_buttonlink` | +| `core/media-text` | `t3bs_media` | +| `core/quote` / `core/pullquote` | `textmedia` with the `Frameless` layout + `element_classes = blockquote` — or a custom `Testimonial` CE if the visual is testimonial-style | +| `core/gallery` | `wapplersystems/filecollection-gallery` | +| WP CPT (e.g. portfolio item) | A TYPO3 news/blog/address record + custom record sitemap, **not** a CE | +| ACF flexible content / page builder layouts | Map each layout to the closest `t3bs_*` container; complex one-offs go in `t3bs_container` with `html` CE child | + +### From Joomla + +| Joomla pattern | Target | +| --- | --- | +| Joomla article body | `textmedia` (one per paragraph block, or one big with embedded media) | +| Module position | A page-level colPos in T3Bootstrap; map module → CE per the rest of this table | +| K2 / EasyBlog items | `news` records (via `t3bootstrap/news`) or `blog` posts (`t3bootstrap/blog`) | +| RSGallery / Phoca Gallery | `wapplersystems/filecollection-gallery` | +| RSForm / Chronoforms | EXT:form via `t3bootstrap/form` set | +| Sliders (DJ-ImageSlider, Smart Slider) | Pick a `ws-slider-*` variant per the brief (most projects use `swiper` or `flexslider`) | +| Modal / accordion modules | `t3bs_accordion` / `t3bs_tabs` | +| YOOtheme widget kit | Map each widget to its `t3bs_*` cousin; for icon + heading + text use `textmedia` with `t3bs_icon` | + +### From older TYPO3 versions + +#### From v9–v12 with `flux_bs5_templates` + +**Use the upgrade wizard.** Don't migrate manually — the wizard preserves child relations, +maps FlexForm values to the new dedicated `tx_t3bs_*` columns, and updates `tx_container_parent`. + +```bash +# In DDEV, after composer-installing t3bootstrap/container-bs5-templates: +ddev exec vendor/bin/typo3 upgrade:run t3bsContainerBs5MigrateFluxBs5 + +# Or via the Install Tool: TYPO3 backend → Admin Tools → Upgrade → Upgrade Wizards +# → "Migrate flux_bs5_templates content elements to container_bs5_templates" → Execute +``` + +**Mapping the wizard applies** (source `vendor/t3bootstrap/container-bs5-templates/Classes/Updates/MigrateFluxBs5ToContainerBs5.php`): + +| Old Flux CType | New CType | Notes | +| --- | --- | --- | +| `fluxbs5templates_container` | `t3bs_container` | FlexForm → `tx_t3bs_container_type` | +| `fluxbs5templates_example` | `t3bs_example` | | +| `fluxbs5templates_alert` | `t3bs_alert` | FlexForm → `tx_t3bs_alert_class`, `tx_t3bs_alert_block`, `tx_t3bs_alert_close_text` | +| `fluxbs5templates_well` | `t3bs_well` | FlexForm → `tx_t3bs_well_style` | +| `fluxbs5templates_panel` | `t3bs_panel` | FlexForm → `tx_t3bs_panel_class` | +| `fluxbs5templates_thumbnail` | `t3bs_thumbnail` | | +| `fluxbs5templates_media` | `t3bs_media` | | +| `fluxbs5templates_card` | `t3bs_card` | | +| `fluxbs5templates_cards` | `t3bs_cards` | FlexForm → `tx_t3bs_cards_layout`, `_grid`, `_gutter` | +| `fluxbs5templates_buttongroup` | `t3bs_buttongroup` | FlexForm → `tx_t3bs_bg_type` | +| `fluxbs5templates_accordion` | `t3bs_accordion` | Children rewired to `t3bs_accordion_item`, colPos 21011 | +| `fluxbs5templates_tabs` | `t3bs_tabs` | Children to `t3bs_tab_item`, colPos 21021. FlexForm → `tx_t3bs_tabs_mode`, `_direction`, `_stackable`, `_position` | +| `fluxbs5templates_carousel` | `t3bs_carousel` | Children to `t3bs_carousel_item`, colPos 21031. FlexForm → controls, indicators, interval, id, color | +| `fluxbs5templates_fluidrow` | `t3bs_fluidrow` | Each flex column becomes a `t3bs_column` child at colPos 21041 | +| `fluxbs5templates_buttonlink` | `t3bs_buttonlink` | FlexForm → full set of `tx_t3bs_button_*` fields | +| `fluxbs5templates_megamenu` | `t3bs_megamenu` | Children to `t3bs_megamenu_item`, colPos 21061 | + +**After the wizard runs:** flush caches (`cache:flush`), update the reference index +(`referenceindex:update`), and re-import freshly delta-migrated content from live if you +were doing the parallel live→stage migration described in the global instructions. + +#### From v8–v11 with `gridelements` + +No wizard. Map manually: + +| `gridelements_pi1` layout | Target | +| --- | --- | +| 1-column grid | `t3bs_container` | +| 2-column grid | `t3bs_fluidrow` + 2× `t3bs_column` | +| 3/4-column grid | `t3bs_fluidrow` + N× `t3bs_column` | +| Tabbed grid | `t3bs_tabs` + N× `t3bs_tab_item` | +| Accordion grid | `t3bs_accordion` + N× `t3bs_accordion_item` | +| Hero grid | `Hero` (from `t3bootstrap/hero-item`) | + +In a migration project: SQL-query `tt_content WHERE CType='gridelements_pi1'`, group by +`tx_gridelements_backend_layout`, and rewrite the CType + children's `colPos` (gridelements +uses `tx_gridelements_columns` instead of `tx_container_parent`). + +#### From v8–v11 with `dce` + +No wizard. DCE content elements are project-specific. For each DCE: + +1. Look at its rendering template — what HTML does it emit? +2. Pick the closest `t3bs_*` CE. +3. Map DCE fields → `tx_t3bs_*` columns or extended `tt_content` fields from `t3bootstrap/core`. +4. Custom-only DCEs that have no direct equivalent → wrap in `t3bs_container` with an `html` child for the unique markup, plan to replace gradually. + +#### From v11/v12 with `bootstrap_package` + +`bootstrap_package` has a different content element set. Mapping: + +| `bootstrap_package` CType | T3Bootstrap CType | +| --- | --- | +| `bootstrap_package_card_group` | `t3bs_cards` | +| `bootstrap_package_carousel` | `t3bs_carousel` | +| `bootstrap_package_accordion` | `t3bs_accordion` | +| `bootstrap_package_tabs` | `t3bs_tabs` | +| `bootstrap_package_modal` | No direct CE — use `t3bs_buttonlink` with `tx_t3bs_button_modal = 1` and a referenced text block | +| `bootstrap_package_listgroup` | `bullets` with `element_classes = list-group` | +| `bootstrap_package_panel` | `t3bs_panel` | +| `bootstrap_package_jumbotron` | `Hero` (`t3bootstrap/hero-item`) | +| `bootstrap_package_textmedia` | core `textmedia` | +| `bootstrap_package_icon_group` | `t3bs_fluidrow` + `t3bs_column` per icon, `textmedia` with `tx_t3bs_icon` set inside each | + +`bootstrap_package`'s `bootstrap_package_card_group` already uses child elements wired via +TCA — copy the child relationships rather than rebuild. + +## When to Pick Which Layer (Cheat Sheet) + +``` +Is it a wrapping/grouping element (multiple CEs sit inside)? +├── YES → container CE +│ ├── styled box around children? → t3bs_card / t3bs_alert / t3bs_panel / t3bs_well +│ ├── tabs/accordion/carousel? → t3bs_tabs / t3bs_accordion / t3bs_carousel +│ ├── timeline? → t3bs_timeline +│ ├── grid? → t3bs_fluidrow + t3bs_column +│ ├── button group? → t3bs_buttongroup +│ └── transparent / just structure? → t3bs_container +│ +└── NO → single CE + ├── prose + maybe one image? → textmedia + ├── headline only? → header + ├── image-only? → image + ├── list? → bullets + ├── table? → table + ├── hero banner? → Hero (hero-item) + ├── card without nested CEs? → t3bsCard (card-element) + ├── before/after slider? → t3bsCompareSlider + ├── animated number? → t3bsCounterbar + ├── countdown? → t3bsCountdown + ├── testimonial? → wapplersystems/testimonials + ├── slider with image-only slides? → ws-slider variant + ├── button-only? → t3bs_buttonlink (standalone) + └── still no fit? → html (temporary, plan to re-classify) +``` + +## colPos Map (Reference) + +When debugging "where did my child go?" — the colPos numbers reserved by +`t3bootstrap/container-bs5-templates`: + +``` +21001 t3bs_container (single slot) +21002 t3bs_example +21003 t3bs_alert +21004 t3bs_well +21005 t3bs_panel +21006 t3bs_thumbnail +21007 t3bs_media +21008 t3bs_card +21009 t3bs_cards +21010 t3bs_buttongroup (allows only t3bs_buttonlink) +21011 t3bs_accordion (allows only t3bs_accordion_item) +21019 t3bs_accordion_item +21021 t3bs_tabs (allows only t3bs_tab_item) +21029 t3bs_tab_item +21031 t3bs_carousel (allows only t3bs_carousel_item) +21039 t3bs_carousel_item +21041 t3bs_fluidrow (allows only t3bs_column) +21050 t3bs_column +21061 t3bs_megamenu (allows only t3bs_megamenu_item) +21069 t3bs_megamenu_item +21071 t3bs_timeline (allows only t3bs_timeline_item) +21079 t3bs_timeline_item +``` + +`tx_container_parent` on a child row stores the parent's `uid`; the child's `colPos` is set +to the parent's slot. Direct SQL example: + +```sql +-- All children of accordion uid=42 +SELECT uid, CType, header, colPos, tx_container_parent +FROM tt_content +WHERE tx_container_parent = 42 AND colPos = 21011; +``` + +## Content Defender Hints (TYPO3 v14.1+) + +`content_defender` is part of TYPO3 v14.1 Core. The container declarations in +`t3bootstrap/container-bs5-templates` already use the `allowed` constraint on the slot +arrays: + +```php +$registerContainer($registry, 't3bs_accordion', $ll . 'ce.accordion', + [[['name' => $ll . 'slot.item', 'colPos' => 21011, 'allowed' => ['CType' => 't3bs_accordion_item']]]], + ... +); +``` + +This automatically enforces the "tabs only contain tab items" / "accordions only contain +accordion items" rules — editors can't accidentally drop a `textmedia` into an accordion's +item slot. **Don't override this in PageTSconfig unless you have a deliberate reason.** + +For a customer-specific column (e.g. "this colPos accepts only Hero or Card"), add the +constraint in your customer extension's PageTSconfig: + +```typoscript +# local_packages//site_/Configuration/page.tsconfig +mod.web_layout.tt_content.colPos_list := addToList(2) +mod.web_layout.BackendLayouts.HeroPage.config.backend_layout.rows.1.columns.1.allowed.CType = t3bootstrap_hero,t3bsCard +``` + +## Migration Workflow + +``` +1. Inventory the source. + - WP: `wp post list`, screenshot blocks per post type + - TYPO3: `SELECT CType, COUNT(*) FROM tt_content GROUP BY CType` (use claude-diagnostics' database:query) + - Static: scrape the rendered pages, categorize by visual pattern +2. Build the mapping table (use the decision matrix above). +3. For flux_bs5_templates → ALWAYS the upgrade wizard. Don't re-do this work. +4. For other sources, write the new content in the TYPO3 backend with the right CTypes, + or script the import via a custom Symfony Command. +5. After import: cache:flush, referenceindex:update, visually compare against the source. +6. For each migrated CE, check: is the visual right? If not — is it a missing CE field + (use the t3bootstrap-overrides skill for template tweaks) or a wrong CE choice + (re-evaluate)? +``` + +## Anti-Patterns + +| Anti-pattern | Why it's bad | Correct approach | +| --- | --- | --- | +| Migrating all content as `html` CE | Loses TYPO3 semantics, FAL, link handling, accessibility | Use the decision matrix; `html` is the last resort, not the first | +| Hand-migrating `flux_bs5_templates` content | Tedious, error-prone, breaks parent-child relations | Run the `t3bsContainerBs5MigrateFluxBs5` wizard | +| Using `t3bs_container` for prose | Renders an empty Bootstrap container wrapper for no reason | Use `textmedia` directly; the page template already wraps it | +| Stacking `t3bs_container` inside `t3bs_container` | Nested Bootstrap containers cause padding/margin compounding bugs | Use `t3bs_fluidrow` + `t3bs_column` to subdivide instead | +| Placing `t3bs_buttonlink` inside a textmedia bodytext as RTE link | Looks like a button in the editor, won't get the Bootstrap classes | Use the `t3bs_buttonlink` CE next to the textmedia, or a `t3bs_buttongroup` if multiple | +| Migrating a multi-image gallery as N separate `image` CEs | Loses the gallery's grid relationship | `wapplersystems/filecollection-gallery` set + `file_collection` CE | +| Creating one `t3bs_card` per card in a 3-card grid | Each card is its own outer container | One `t3bs_cards` parent with three child content elements (one per card) — `tx_t3bs_cards_layout = grid-cards` for the grid | + +## Reference Files in This Project + +```text +vendor/t3bootstrap/container-bs5-templates/Configuration/TCA/Overrides/tt_content.php # all t3bs_* registrations + ContainerConfiguration +vendor/t3bootstrap/container-bs5-templates/Classes/Updates/MigrateFluxBs5ToContainerBs5.php # the upgrade wizard +vendor/t3bootstrap/container-bs5-templates/Resources/Private/Templates/ # CE Fluid templates +vendor/t3bootstrap/core/Configuration/TCA/Overrides/10_tt_content.php # extended tt_content fields (image_classes, element_classes, imagecols_grid, background_media) +vendor/t3bootstrap/core/Configuration/TCA/Overrides/600_content_element_card.php # t3bsCard standalone +vendor/t3bootstrap/core/Configuration/TCA/Overrides/600_content_element_compare_slider.php +vendor/t3bootstrap/core/Configuration/TCA/Overrides/600_content_element_countdown.php +vendor/t3bootstrap/core/Configuration/TCA/Overrides/202_content_element_counterbar.php +vendor/t3bootstrap/hero-item/Configuration/TCA/Overrides/tt_content.php # Hero CType +``` + +For overriding any of these CEs (different colors, additional fields, replaced templates), +switch to the **`t3bootstrap-overrides`** skill. + +For project bootstrapping (required composer packages, site sets), see the +**`t3bootstrap-site-package`** skill. \ No newline at end of file diff --git a/.agents/skills/t3bootstrap-content-elements/agents/openai.yaml b/.agents/skills/t3bootstrap-content-elements/agents/openai.yaml new file mode 100644 index 0000000..f033e03 --- /dev/null +++ b/.agents/skills/t3bootstrap-content-elements/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "T3Bootstrap Content Elements" + short_description: "Pick the right CE when migrating content to T3Bootstrap" + default_prompt: "Use $t3bootstrap-content-elements to choose which TYPO3 content element fits a piece of source content — the 23 t3bs_* container CEs, the custom single CEs (Card, CompareSlider, Countdown, Counterbar, Hero, Teaser, Testimonial), and the right CType-mapping for static HTML, WordPress blocks, Joomla modules, gridelements, dce, bootstrap_package, and the flux_bs5_templates → container_bs5_templates upgrade wizard." \ No newline at end of file diff --git a/.agents/skills/t3bootstrap-overrides/SKILL.md b/.agents/skills/t3bootstrap-overrides/SKILL.md new file mode 100644 index 0000000..1d4124b --- /dev/null +++ b/.agents/skills/t3bootstrap-overrides/SKILL.md @@ -0,0 +1,519 @@ +--- +name: t3bootstrap-overrides +description: Customize a T3Bootstrap-based TYPO3 v14 project minimally — without forking the upstream `t3bootstrap/*` packages. Covers the four override surfaces in order of invasiveness (Site Settings / TypoScript constants → SCSS `!default` variables → Fluid `templateRootPaths.10` overrides → custom DataProcessors), how to decide which surface fits a given change, the path-index conventions (`0` core, `5` core extensions, `10` site-package, `100` container-bs5, `200` Frameless), and the standard layout for a customer-specific extension that ships such overrides. Use this skill whenever the customer wants different colors, fonts, page structure, navigation, footer, content-element rendering, or any other deviation from upstream T3Bootstrap defaults. +--- + +# T3Bootstrap Overrides + +Use this skill when customizing a TYPO3 v14 project on the T3Bootstrap stack. The goal: +**touch nothing in `vendor/`**, ship the customization as a thin customer extension, keep +`composer update` upgrade-safe. + +> **Companion skill:** [`t3bootstrap-site-package`](../t3bootstrap-site-package/SKILL.md) explains the underlying setup. Start there if the project isn't bootstrapped yet. + +## Override Surfaces — Pick the Least Invasive + +T3Bootstrap exposes **four layers** of customization. Always start with the highest layer +that can solve the problem; descend only when forced. + +``` +1. Site Settings / TypoScript constants ← most upgrade-safe, no code +2. SCSS !default variable overrides ← brand colors, fonts, spacing tokens +3. Fluid template overrides via index 10 ← structural HTML changes +4. Custom DataProcessor / PHP ← only when data shape must change +``` + +### Decision table + +| Need | Layer | Example | +| --- | --- | --- | +| Change brand color | 2 (SCSS) or 1 (CSS custom property) | Re-define `$primary` | +| Change header height / sticky behavior | 1 (constants) | `header.sticky` setting | +| Show/hide language menu | 1 (constants) | `navigation.languageMenu` | +| Different button styling | 1 + 2 | CSS custom property, or `$btn-padding-y` SCSS | +| Add an extra CSS class to all CEs | 3 (override Content layout) | `Resources/Private/Layouts/Content/Default.html` | +| Replace the Header partial with something completely different | 3 (override partial) | `Resources/Private/Partials/PageHeader.html` | +| Change the data passed to a CE | 4 (custom DataProcessor) | Replace `FrameClassesProcessor` | + +If a question is at the boundary (e.g. "is this an SCSS change or a Fluid override?"), +the answer is usually: **whichever is closer to the top of the table**. + +## Layer 1 — Site Settings / TypoScript Constants + +The `t3bootstrap/template` set exposes a large set of constants under +`Configuration/TypoScript/constants.typoscript`. Categories include navigation, language, +text, font, search box, logo, banner, body tag, breadcrumb, color, color-mode, language +defaults, screen, domains. + +### Where they're defined + +```text +vendor/t3bootstrap/template/Configuration/TypoScript/constants.typoscript # the master file (~639 lines) +vendor/t3bootstrap/template/Configuration/TypoScript/Plugin/Constants/*.typoscript # per-plugin (calendarize, indexedsearch, …) +``` + +### How to override + +#### Best — Site Setting in the TYPO3 v14 Backend +Site Settings module → "T3Bootstrap Template" category → change the value. Persisted to +`config/sites//settings.yaml` automatically. Reviewable in git. + +#### Better — customer extension's Site Set +Settings shipped from a customer extension's `Configuration/Sets//settings.yaml`: + +```yaml +# local_packages//site_/Configuration/Sets/Site/settings.yaml +plugin.tx_template.view.templateRootPath: 'EXT:site_/Resources/Private/Templates/' +plugin.tx_template.view.partialRootPath: 'EXT:site_/Resources/Private/Partials/' +plugin.tx_template.view.layoutRootPath: 'EXT:site_/Resources/Private/Layouts/' + +# Color tokens at the constants level (CSS custom property variables will see them) +color.primary: '#005262' +color.secondary: '#55aa63' +``` + +#### Acceptable — TypoScript constants in a customer extension +Only if you need conditions or includes that the Settings YAML can't express: + +```typoscript +# local_packages//site_/Configuration/Sets/Site/constants.typoscript +plugin.tx_template.view.templateRootPath = EXT:site_/Resources/Private/Templates/ +``` + +> **Never** edit the upstream constants file. Any constant you want to change has a +> matching site setting key — find it in the backend Constants Editor or in the master +> constants file's `# cat=…` comments. + +## Layer 2 — SCSS Variable Overrides + +T3Bootstrap's SCSS is built around Bootstrap 5 and uses `!default` on every variable so +customer SCSS loaded **before** the vendor file wins. + +### The compilation pipeline + +The stack relies on `wapplersystems/ws-scss` (or any project-set-up Node/Vite/PostCSS +pipeline). The conventional entry point is one customer SCSS file that: + +1. Defines overrides +2. Imports the t3bootstrap base +3. Imports Bootstrap 5 +4. Adds any extra component CSS + +### Master variables file + +```text +vendor/t3bootstrap/template/Resources/Public/SCSS/_variables.scss +``` + +Representative excerpt — **all values are `!default`, so you can re-set them upstream**: + +```scss +$primary: #005262 !default; +$secondary: #55aa63 !default; +$lightblue: #87c8e6 !default; + +$prefix: bs- !default; + +$custom-colors: ( + dark-gray: #005262, + gray: #999, + graylight: #f7f7f7 +) !default; + +$icon-sizes: ( + sm: 2rem, + md: 3rem, + lg: 4rem, + xl: 6rem +) !default; + +$font-family-sans-serif: 'Open Sans', "Helvetica Neue", Helvetica, Arial, sans-serif !default; +$h1-font-size: 2.0rem !default; +$h2-font-size: 1.8rem !default; +$font-size-base: 0.95rem !default; +``` + +Related files in the same directory: `_colors.scss`, `_mixins.scss`, `_mmenu.scss`, +`_variables-dark.scss`, `_variables-contrast-high.scss`, and the subdirectories +`additions/`, `aos/`, `aria/`, `base-layout/`, `bootstrap/`, `components/`, `effects/`, +`elements/`, `extensions/`, `fonts/`, `mixins/`. + +### Customer override pattern + +```scss +/* local_packages//site_/Resources/Public/Scss/main.scss */ + +/* 1. Re-define variables BEFORE importing t3bootstrap */ +$primary: #c81d25; +$secondary: #2b6cb0; +$font-family-sans-serif: 'Inter', sans-serif; + +$custom-colors: ( + brand-dark: #1a1a2e, + brand-light: #f5f5f5, +); + +/* Bootstrap defaults can also be redefined here, BEFORE bootstrap loads */ +$btn-border-radius: 0; + +/* 2. Import the t3bootstrap base — this brings in Bootstrap 5 and all defaults */ +@import 'EXT:template/Resources/Public/SCSS/base-layout'; + +/* 3. Project-specific additions */ +.c-customer-banner { + background: $primary; + /* … */ +} +``` + +`@import 'EXT:template/...'` works inside `ws_scss` — it resolves `EXT:` paths via the +TYPO3 path resolver. If your toolchain doesn't, use a relative or absolute path. + +### When to use CSS custom properties instead + +Bootstrap 5 ships **CSS custom properties** (`--bs-primary`, `--bs-body-color`, …) for +runtime themability. T3Bootstrap respects them. + +```css +/* As an inline-style block on the page, or in a separate stylesheet loaded after Bootstrap */ +:root { + --bs-primary: #c81d25; + --bs-secondary: #2b6cb0; +} +``` + +**Rule of thumb:** values that must be available at runtime (dark mode, theme switching, +editor-tweaked colors via Site Settings) → CSS custom properties. Values fixed at build +time (font stack, spacing scale, breakpoints) → SCSS variables. + +## Layer 3 — Fluid Template Overrides via Path Index + +Every Fluid-rendering object in T3Bootstrap exposes `templateRootPaths`, `partialRootPaths`, +and `layoutRootPaths` as numerically-indexed arrays. **Higher index wins.** The convention +is: + +| Index | Owner | Purpose | +| --- | --- | --- | +| `0` | t3bootstrap base | Original templates. Never touch. | +| `5` | `t3bootstrap/core` extension overrides | E.g. core's `fluid_styled_content` overrides. | +| `9` / `40` | `t3bootstrap/template` plugin-specific overrides | Felogin, fluid_styled_content, indexed_search etc. | +| `10` | **Customer site package** | This is the slot you write to. | +| `100` | `t3bootstrap/container-bs5-templates` | The `t3bs_*` CE templates. | +| `200` | Frameless variant from `container-bs5-templates` | `Frameless/` layout dir for the "no-frame" CE option. | + +### The page template's override hook + +`vendor/t3bootstrap/template/Configuration/TypoScript/HTML/Page/body.typoscript` (lines +77–90): + +```typoscript +layoutRootPaths { + 0 = EXT:template/Resources/Private/Layouts/ + 10 = {$plugin.tx_template.view.layoutRootPath} +} +templateRootPaths { + 0 = EXT:template/Resources/Private/Templates/Page/ + 10 = {$plugin.tx_template.view.templateRootPath} +} +partialRootPaths { + 0 = EXT:template/Resources/Private/Partials/ + 10 = {$plugin.tx_template.view.partialRootPath} +} +``` + +Configure `plugin.tx_template.view.*` via Site Settings (preferred) or constants — see +Layer 1. + +### What's in `Resources/Private/Templates/Page/` upstream + +``` +1Column.html +2Columns.html +2Columns2.html +3Columns.html +Onepager.html +``` + +These are picked by the page's **backend layout**. Override one by copying it to +`local_packages//site_/Resources/Private/Templates/2Columns.html` and +modifying. + +### Layouts upstream + +``` +Resources/Private/Layouts/ +├── Content.html # The wrapper used by all fluid_styled_content CEs +└── Page.html # The HTML shell … +``` + +### Partials upstream — what's worth overriding + +``` +Resources/Private/Partials/ +├── BackgroundMedia.html +├── Footer/ ← whole footer block partials (columns, copyright bar) +├── Header/ ← header column partials +├── Navigation/ ← main nav, breadcrumb, language menu, mobile burger +├── Hero.html +├── Navigation.html +├── PageFooter.html +├── PageHeader.html +├── Searchbox.html +└── Skiplinks.html +``` + +#### Common override targets + +| Want to change | Override | +| --- | --- | +| Site-wide header structure | `Resources/Private/Partials/PageHeader.html` | +| Logo placement, sticky behavior | `Resources/Private/Partials/Header/*.html` | +| Navigation rendering | `Resources/Private/Partials/Navigation.html` + `Navigation/*.html` | +| Search box markup | `Resources/Private/Partials/Searchbox.html` | +| Footer layout | `Resources/Private/Partials/PageFooter.html` + `Footer/*.html` | +| Skiplinks for a11y | `Resources/Private/Partials/Skiplinks.html` | +| Hero markup | `Resources/Private/Partials/Hero.html` | +| Background-media wrapper | `Resources/Private/Partials/BackgroundMedia.html` | + +> Copy the **whole** partial, then modify. Fluid does not "merge" — once index 10 +> shadows index 0, it must contain the full template. + +## Overriding `fluid_styled_content` CEs + +The stack already overrides core's `fluid_styled_content` at index `9` (template) and `5` +(layouts) via `t3bootstrap/core`. To override **further**, point to a higher index: + +```typoscript +# In a customer extension's setup.typoscript +lib.contentElement.templateRootPaths.20 = EXT:site_/Resources/Private/ContentElements/Templates/ +lib.contentElement.partialRootPaths.20 = EXT:site_/Resources/Private/ContentElements/Partials/ +lib.contentElement.layoutRootPaths.20 = EXT:site_/Resources/Private/ContentElements/Layouts/ +``` + +Upstream-rendered CTypes you can override there: + +```text +vendor/t3bootstrap/core/Resources/Private/Extensions/fluid_styled_content/Templates/ +├── Textmedia.html +├── Textpic.html +├── Image.html +├── Table.html +├── Shortcut.html +├── Card.html +├── CardsCarousel.html +├── Counterbar.html +├── Countdown.html +├── CompareSlider.html +└── NumberCarousel.html +``` + +For the `t3bs_*` container-based CEs use the **container_bs5_templates** path indices: + +```typoscript +# Override e.g. the Card.html template for t3bs_card +lib.t3bsContent.templateRootPaths.200 = EXT:site_/Resources/Private/T3bsContent/Templates/ +lib.t3bsContent.partialRootPaths.200 = EXT:site_/Resources/Private/T3bsContent/Partials/ +``` + +Source: + +```text +vendor/t3bootstrap/container-bs5-templates/Resources/Private/Templates/ +├── Card.html ← t3bs_card +├── Cards.html ← t3bs_cards +├── Accordion.html ← t3bs_accordion +├── AccordionItem.html +├── Tabs.html +├── TabItem.html +├── Carousel.html +├── CarouselItem.html +├── Container.html +├── FluidRow.html +├── Column.html +├── Alert.html +├── Panel.html +├── Well.html +├── Thumbnail.html +├── Media.html +├── Example.html +├── Timeline.html +├── TimelineItem.html +├── Buttongroup.html +├── Buttonlink.html +├── Megamenu.html +└── MegamenuItem.html +``` + +## Layer 4 — Custom DataProcessors + +Only needed when you want to **change the data shape** the Fluid templates see, not just +the markup. Examples: + +- Add a derived field (e.g. a "read time" estimate for news items). +- Replace `T3Bootstrap\Core\Frontend\DataProcessing\FrameClassesProcessor` with a custom one that emits different frame CSS classes. +- Add a side-effect (logging, prefetching, conditional caching of related data). + +Upstream processors used by `lib.t3bsContent`: + +```text +T3Bootstrap\Core\Frontend\DataProcessing\IconProcessor # 706 +T3Bootstrap\Core\Frontend\DataProcessing\FrameClassesProcessor # 707 +T3Bootstrap\Core\Frontend\DataProcessing\AosDataAttributeProcessor # 708 +T3Bootstrap\Core\Frontend\DataProcessing\ProcessedDataToRegisterProcessor # 900 +``` + +Plus core's `TYPO3\CMS\Frontend\DataProcessing\FilesProcessor` at 703 for `background_media`. + +### Adding a custom processor + +```typoscript +# Customer Site Set setup.typoscript +lib.t3bsContent.dataProcessing.950 = Vendor\SiteCustomer\DataProcessing\ReadTimeProcessor +lib.t3bsContent.dataProcessing.950.target = readTime +``` + +### Replacing an upstream processor + +```typoscript +# Replace at the same index — TypoScript merges shallow, so reassign: +lib.t3bsContent.dataProcessing.707 = Vendor\SiteCustomer\DataProcessing\CustomFrameClassesProcessor +lib.t3bsContent.dataProcessing.707 > +lib.t3bsContent.dataProcessing.707 = Vendor\SiteCustomer\DataProcessing\CustomFrameClassesProcessor +``` + +The `>` operator clears the original config sub-tree first to prevent stray options. + +### TSFE-aware processors + +On TYPO3 v14 there is no more `$GLOBALS['TSFE']`. T3Bootstrap's processors use the +**`RegisterUtility`** static registry at `T3Bootstrap\Core\Utility\RegisterUtility` to +share per-request state across processors and ViewHelpers. If your processor needs to +publish a value for a later ViewHelper, write to that registry — don't reach for the +removed TSFE register. + +## Standard Customer-Extension Layout + +``` +local_packages//site_/ +├── composer.json +├── ext_emconf.php +├── ext_localconf.php +├── Classes/ +│ ├── DataProcessing/ # Layer 4 +│ └── ViewHelpers/ +├── Configuration/ +│ ├── Sets/ +│ │ └── Site/ +│ │ ├── config.yaml # dependencies +│ │ ├── settings.yaml # Layer 1 +│ │ ├── constants.typoscript # Layer 1 (only if Settings YAML insufficient) +│ │ └── setup.typoscript # Layer 3 path-index registration + Layer 4 processors +│ └── TCA/Overrides/ +└── Resources/ + ├── Private/ + │ ├── Templates/ # Page templates (Layer 3) + │ ├── Layouts/ # Page + content layouts + │ ├── Partials/ # Header/Footer/Nav/Hero/… (Layer 3) + │ ├── ContentElements/ # fluid_styled_content overrides + │ │ ├── Templates/ + │ │ ├── Layouts/ + │ │ └── Partials/ + │ ├── T3bsContent/ # t3bs_* CE overrides + │ │ ├── Templates/ + │ │ └── Partials/ + │ └── Language/ + └── Public/ + ├── Scss/ + │ ├── main.scss # Layer 2 — !default overrides + imports + │ └── _customer-variables.scss + ├── Css/ # compiled output + └── JavaScript/ +``` + +`Configuration/Sets/Site/config.yaml`: + +```yaml +name: /site- +label: Site Customization +dependencies: + - t3bootstrap/template + - t3bootstrap/container-bs5-templates +``` + +`setup.typoscript`: + +```typoscript +# Page templates +plugin.tx_template { + view { + templateRootPath = EXT:site_/Resources/Private/Templates/ + partialRootPath = EXT:site_/Resources/Private/Partials/ + layoutRootPath = EXT:site_/Resources/Private/Layouts/ + } +} + +# fluid_styled_content extensions +lib.contentElement { + templateRootPaths.20 = EXT:site_/Resources/Private/ContentElements/Templates/ + partialRootPaths.20 = EXT:site_/Resources/Private/ContentElements/Partials/ + layoutRootPaths.20 = EXT:site_/Resources/Private/ContentElements/Layouts/ +} + +# t3bs_* CEs +lib.t3bsContent { + templateRootPaths.200 = EXT:site_/Resources/Private/T3bsContent/Templates/ + partialRootPaths.200 = EXT:site_/Resources/Private/T3bsContent/Partials/ +} + +# Custom DataProcessor +lib.t3bsContent.dataProcessing.950 = Vendor\SiteCustomer\DataProcessing\ReadTimeProcessor +``` + +Then add the customer set to the site config: + +```yaml +# config/sites//config.yaml +dependencies: + - t3bootstrap/template + - t3bootstrap/container-bs5-templates + - /site- # <- new +``` + +## Anti-Patterns + +| Anti-pattern | Why it's bad | Correct approach | +| --- | --- | --- | +| Editing files under `vendor/t3bootstrap/*/` | Composer wipes changes on next update | Override via the customer extension's path index `10` (or higher) | +| Forking `t3bootstrap/template` in customer's GitHub | Loses upstream fixes; merge conflicts forever | Customer extension + targeted partial overrides | +| Adding raw `