Files
t3bootstrap-skills/.agents/skills/t3bootstrap-overrides/SKILL.md
T
wappler af9368177b 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/<name>/{SKILL.md,agents/openai.yaml}).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-15 09:58:33 +02:00

21 KiB
Raw Blame History

name, description
name description
t3bootstrap-overrides 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 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

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/<site>/settings.yaml automatically. Reviewable in git.

Better — customer extension's Site Set

Settings shipped from a customer extension's Configuration/Sets/<Customer>/settings.yaml:

# local_packages/<customer>/site_<customer>/Configuration/Sets/Site<Customer>/settings.yaml
plugin.tx_template.view.templateRootPath: 'EXT:site_<customer>/Resources/Private/Templates/'
plugin.tx_template.view.partialRootPath:  'EXT:site_<customer>/Resources/Private/Partials/'
plugin.tx_template.view.layoutRootPath:   'EXT:site_<customer>/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:

# local_packages/<customer>/site_<customer>/Configuration/Sets/Site<Customer>/constants.typoscript
plugin.tx_template.view.templateRootPath = EXT:site_<customer>/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

vendor/t3bootstrap/template/Resources/Public/SCSS/_variables.scss

Representative excerpt — all values are !default, so you can re-set them upstream:

$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

/* local_packages/<customer>/site_<customer>/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.

/* 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 7790):

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/<customer>/site_<customer>/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 <html><body>…

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:

# In a customer extension's setup.typoscript
lib.contentElement.templateRootPaths.20 = EXT:site_<customer>/Resources/Private/ContentElements/Templates/
lib.contentElement.partialRootPaths.20  = EXT:site_<customer>/Resources/Private/ContentElements/Partials/
lib.contentElement.layoutRootPaths.20   = EXT:site_<customer>/Resources/Private/ContentElements/Layouts/

Upstream-rendered CTypes you can override there:

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:

# Override e.g. the Card.html template for t3bs_card
lib.t3bsContent.templateRootPaths.200 = EXT:site_<customer>/Resources/Private/T3bsContent/Templates/
lib.t3bsContent.partialRootPaths.200  = EXT:site_<customer>/Resources/Private/T3bsContent/Partials/

Source:

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:

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

# Customer Site Set setup.typoscript
lib.t3bsContent.dataProcessing.950 = Vendor\SiteCustomer\DataProcessing\ReadTimeProcessor
lib.t3bsContent.dataProcessing.950.target = readTime

Replacing an upstream processor

# 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/<customer>/site_<customer>/
├── composer.json
├── ext_emconf.php
├── ext_localconf.php
├── Classes/
│   ├── DataProcessing/                 # Layer 4
│   └── ViewHelpers/
├── Configuration/
│   ├── Sets/
│   │   └── Site<Customer>/
│   │       ├── 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<Customer>/config.yaml:

name: <customer>/site-<customer>
label: <Customer> Site Customization
dependencies:
  - t3bootstrap/template
  - t3bootstrap/container-bs5-templates

setup.typoscript:

# Page templates
plugin.tx_template {
  view {
    templateRootPath = EXT:site_<customer>/Resources/Private/Templates/
    partialRootPath  = EXT:site_<customer>/Resources/Private/Partials/
    layoutRootPath   = EXT:site_<customer>/Resources/Private/Layouts/
  }
}

# fluid_styled_content extensions
lib.contentElement {
  templateRootPaths.20 = EXT:site_<customer>/Resources/Private/ContentElements/Templates/
  partialRootPaths.20  = EXT:site_<customer>/Resources/Private/ContentElements/Partials/
  layoutRootPaths.20   = EXT:site_<customer>/Resources/Private/ContentElements/Layouts/
}

# t3bs_* CEs
lib.t3bsContent {
  templateRootPaths.200 = EXT:site_<customer>/Resources/Private/T3bsContent/Templates/
  partialRootPaths.200  = EXT:site_<customer>/Resources/Private/T3bsContent/Partials/
}

# Custom DataProcessor
lib.t3bsContent.dataProcessing.950 = Vendor\SiteCustomer\DataProcessing\ReadTimeProcessor

Then add the customer set to the site config:

# config/sites/<site-id>/config.yaml
dependencies:
  - t3bootstrap/template
  - t3bootstrap/container-bs5-templates
  - <customer>/site-<customer>   # <- 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 <style> in PageTSconfig to "patch" colors Fragile, gets purged by cache flushes, not visible at build time Layer 2 (SCSS !default) or CSS custom properties
Copying ALL upstream partials into the customer ext "just in case" Now you own every change forever Copy only what you override
Setting templateRootPaths.0 in the customer extension Shadows the original; future upstream changes invisible Use index 10 (or higher), keep 0 for vendor
Cloning the whole vendor SCSS tree Re-doing Bootstrap themability Re-define !default variables in customer SCSS, import vendor base
Hardcoding EXT:template/... paths in the customer ext templates The customer extension shouldn't refer to upstream paths Use Fluid layout/partial inheritance — <f:layout name="Default"/> finds it via layoutRootPaths automatically
Modifying lib.dynamicContent "to add fields" The DataProcessor pipeline is the right hook Add a processor at index 900+

Verification Checklist

After any override change:

  1. ddev exec vendor/bin/typo3 cache:flush — TypoScript and Fluid template paths are cached.
  2. Reload the frontend; if SCSS changed, run the customer's CSS build (npm run build, or ws-scss watcher).
  3. Inspect the rendered HTML — confirm your override is the one rendered (look for a distinctive marker comment you added).
  4. TYPO3 backend → Maintenance → "Render TypoScript" → confirm the right templateRootPaths.10 / .20 / .200 are populated with your paths.
  5. If layered overrides don't seem to take effect: list lib.t3bsContent.templateRootPaths via vendor/bin/typo3 ts:setup --path=lib.t3bsContent.templateRootPaths (from claude-diagnostics) — index values shown in numeric order.

Reference Files in This Project

vendor/t3bootstrap/template/Configuration/TypoScript/constants.typoscript          # all constants
vendor/t3bootstrap/template/Configuration/TypoScript/HTML/Page/body.typoscript     # main layoutRootPaths block
vendor/t3bootstrap/template/Resources/Public/SCSS/_variables.scss                  # SCSS variables
vendor/t3bootstrap/template/Resources/Private/Partials/                            # partials to override
vendor/t3bootstrap/template/Resources/Private/Templates/Page/                      # page templates
vendor/t3bootstrap/core/Resources/Private/Extensions/fluid_styled_content/         # core CE overrides at index 5/9
vendor/t3bootstrap/container-bs5-templates/Configuration/Sets/ContainerBs5Templates/setup.typoscript  # lib.t3bsContent declaration
vendor/t3bootstrap/container-bs5-templates/Resources/Private/Templates/            # t3bs_* CE templates

For CE-by-CE migration mapping and the t3bs_* catalog, see the t3bootstrap-content-elements skill.