wapplersystems / herobuilder
Graphical per-breakpoint hero composition builder (drag & drop FAL layers, carousel of collages) for TYPO3.
Package info
github.com/WapplerSystems/t3-herobuilder
Language:JavaScript
Type:typo3-cms-extension
pkg:composer/wapplersystems/herobuilder
Requires
- php: >=8.3
- typo3/cms-backend: ^14.3
- typo3/cms-core: ^14.3
- typo3/cms-fluid-styled-content: ^14.3
- typo3/cms-frontend: ^14.3
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-28 13:25:30 UTC
README
Graphical, per-breakpoint hero composition builder for TYPO3.
Editors drag & drop FAL image layers, text and linked button layers onto a stage,
position/scale/rotate them per breakpoint, and manage multiple collages that render as a
Bootstrap 5 carousel. Layers stay individual, CSS-positioned elements (%-based) — no
server-side flattening for the frontend — with optional AOS entrance animations.
A 3-column canvas editor (toolbar · layer panel · stage · property tabs, based on Moveable) provides a full WYSIWYG workflow, including reusable templates, one-click social / OG image export (ImageMagick), and a live preview rendered with the real frontend CSS.
- Extension key:
herobuilder - Content element (CType):
herobuilder - TYPO3: 12.4 · PHP: ≥ 8.1
- State: beta (0.1.0)
Table of contents
- Concepts
- Feature list
- Editor GUI
- Templates
- Social / OG image export
- Data model
- Installation
- Configuration
- Keyboard shortcuts
- Architecture
Concepts
- Stage — the hero canvas. Each breakpoint has a fixed aspect ratio (frontend) and a realistic reference device width (backend editor), so what you edit matches the device.
- Breakpoints — Bootstrap 5:
xs(<576px),sm(576–767),md(768–991),lg(992–1199),xl(1200–1399),xxl(≥1400). Every layer is positioned independently per breakpoint. - Collage — one slide. A content element holds n collages → rendered as a carousel.
- Layer — an image (FAL), a text block, or a button (text + TYPO3 link), freely placed within the stage.
- Template — a reusable composition (record in
tx_herobuilder_templateor a shipped preset) that can be applied to a collage or created from one.
Feature list
Content element & rendering
- Dedicated
herobuildercontent element with a New-Content-Element wizard entry. - 1:n collages (
tx_herobuilder_collage) rendered as a Bootstrap 5 carousel (autoplay, interval, loop, prev/next controls, indicators, pause-on-hover, slide/fade transition). - Per-breakpoint stage aspect ratios (frontend) via scoped, media-query CSS — or a fixed stage height per breakpoint, switchable per content element.
- Responsive images — every image layer gets a WebP
srcset(480–3200px, capped at the cropped source) and asizescomputed from its per-breakpoint geometry, including the overflow ofcover(e.g. a wide background on a tall phone stage is painted much wider than the screen). - Accessible carousel — localized labels (
locallang.xlf), carousel/slide roles, a pause/play button whenever autoplay is on (WCAG 2.2.2), no autoplay and no AOS entrance animations underprefers-reduced-motion, autoplay holds while keyboard focus is inside, visible focus rings on the controls. Prev · indicators · next sit together in a dark pill at the bottom centre: never on the composition's text, >= 3:1 contrast on any artwork, 24 px targets. - CSP-ready frontend — the scoped composition CSS goes into the
<head>via the AssetCollector withcsp="true"(hash/nonce), no inline<style>in the body. - Backend page-module preview renderer (slide + layer thumbnails / text chips).
Layers
- Image layers from FAL (native TYPO3 file browser via the sibling
assetsfield → realsys_file_reference, clean reference index). - Text layers — plain text, styled exclusively via predefined classes (safe, auto-escaped).
The HTML element is selectable (
div,p,h1–h6) for semantics only: hero.css resets the element's own styles, so a heading looks exactly like the same layer as adiv. - Button layers — text + TYPO3 link (page/url/file/mail/…) via the native Link Browser,
styled with
btnclasses; rendered as a<a>typolink on the frontend. - Per-breakpoint geometry — x / y / width / height (%), rotation, z-index, visibility.
- Image fit —
fill/cover/contain(no distortion for cover/contain). - Focus point —
object-positionpicker (which part stays visible whencovercrops). - Art direction — "Crop & focus only for XS" gives a breakpoint its own crop and focus point.
The frontend renders a
<picture>whose<source>for that breakpoint carries physically cropped derivatives: a phone loads the phone framing (e.g. 588 px) instead of a desktop image it would mostly crop away. - Predefined CSS classes — multi-select (chips) per layer from a page-TSconfig list (Bootstrap 5 set); validated against the same whitelist on the frontend (injection-safe).
- Darkening overlay behind text — adjustable scrim (0–100 %) for readability over busy images.
- Per-layer TYPO3 link on any layer (native Link Browser).
- AOS entrance animation per layer (effect, delay, duration).
Templates & export
- Templates gallery (“Templates” button) — apply a reusable composition to the current collage; shows rendered thumbnails of shipped presets + user templates.
- Save as template — store the current composition as a
tx_herobuilder_templaterecord. - Social / OG image export — render the composition to a downloadable image in OG, 16:9, Instagram 1:1 / 4:5 and Story 9:16 formats (ImageMagick, cached).
- See Templates and Social / OG image export.
Backend editor (3-column, Moveable-based WYSIWYG)
- Toolbar: title, breakpoint pills (active = accent border), and actions (Add image/text/button, Templates, Save as template, Export image, Live preview toggle).
- Left — Layer panel: rows with color/thumbnail chip, name, eye (visibility) and lock; drag-reorder the z-stack; right-click for actions (duplicate, align, front/back, delete).
- Center — Stage: drag / resize / rotate with snapping & guide lines (stage edges/center
- other layers); zoom badge (Ctrl+wheel cursor-anchored, click = fit ↔ 100 %); pan via scrollbars, middle-mouse or Space+drag.
- Right — Property tabs, fields per layer type: Content (text, HTML element, link — or for images crop/flip/auto-trim/own framing, fit, focus point for cover/contain, alternative text, link), Design (grouped class chips, text darkening), Position (numeric X/Y/W/H/rotation + arrow-key nudging, align icons, visible on this breakpoint, copy to all breakpoints), Animation (effect, delay, duration, replay). Delete via the layer list, context menu or Del.
- Undo / redo (100-step history) + keyboard shortcuts.
Quality & i18n
- Fully localized UI (English source + German
de.translation) — TCA, tabs, buttons, canvas panel, context menu, layer list, zoom, templates, export and preview. - CSP-safe live preview — no inline
<script>(external same-origin JS) and no inline<style>(dynamic CSS delivered as a JSON data island, injected via a constructable stylesheet). Works under a strict backend CSP (script-src 'self', style-src without'unsafe-inline').
Editor GUI
A single field renders the whole editor in a 3-column grid under a toolbar:
[ Toolbar: title · breakpoint pills · Add image/text/button · Templates · Save as template · Export · Live preview ]
[ left ~230px: LAYERS | center: stage (zoom badge) | right ~340px: Transform · Style · Animation ]
- Selecting a layer (in the stage or the layer list) fills the right property panel.
- The Style tab shows only the controls relevant to the layer type (e.g. fit/focus for images, scrim for text, link for all).
- The live preview and the export both reuse the rendering paths described below.
Templates
Reusable compositions applied to a collage or created from one.
- Storage — the
tx_herobuilder_templatetable (records editable like any other TYPO3 record; put them in a sysfolder). Plus shipped, read-only presets as JSON underResources/Private/Templates/Presets/. - Gallery — the “Templates” toolbar button opens a modal with a card per template (rendered 21:9 thumbnail + name). Select a card → “Apply template” replaces the current collage’s layers.
- Save as template — the “Save as template” button stores the current composition as a new
tx_herobuilder_templaterecord (via DataHandler) in the folder configured bytx_herobuilder.templatePid(page TSconfig; falls back to the record’s pid — a sysfolder is recommended, see Configuration). - Thumbnails are generated by
CompositeImageServiceand cached intypo3temp/assets/herobuilder/.
Social / OG image export
The “Export image” button renders the current composition into a downloadable raster image via
ImageMagick (CompositeImageService), cached by a hash of the inputs.
| Format | Size | Use |
|---|---|---|
og |
1200×630 | Open Graph / Facebook / LinkedIn |
wide |
1200×675 | 16:9 (Twitter/X, YouTube) |
square |
1080×1080 | Instagram 1:1 |
portrait |
1080×1350 | Instagram 4:5 |
story |
1080×1920 | Story / Reel 9:16 |
For each format the breakpoint whose design ratio is closest is used, so the layout looks intended.
Image layers are composited with cover/contain/fill; text/button layers are approximated (font size
scales with canvas width, colors/alignment from classes, buttons as a filled box) — CSS-styled text
can’t be reproduced pixel-perfectly by ImageMagick, which is acceptable for share images/thumbnails.
Requires ImageMagick ($GLOBALS['TYPO3_CONF_VARS']['GFX']['processor_path']).
Data model
tt_content (CType herobuilder, carousel settings) → 1:n tx_herobuilder_collage (one row per
slide). Each collage stores its layer geometry in composition (JSON):
{
"layers": [
{
"id": "bg",
"type": "image",
"fileUid": 71877,
"alt": "",
"link": "",
"fit": "cover",
"focusX": 50,
"focusY": 50,
"overlay": 0,
"locked": false,
"anim": { "effect": "fade-up", "delay": 200, "duration": 600 },
"placements": {
"xs": { "x": 0, "y": 0, "w": 100, "h": 100, "rot": 0, "z": 1, "visible": true }
}
},
{
"id": "t1",
"type": "text",
"text": "DEV LAND 2026",
"cssClass": "display-3 text-white fw-bold text-center",
"overlay": 55,
"placements": { "lg": { "x": 20, "y": 60, "w": 60, "h": 12, "rot": 0, "z": 3, "visible": true } }
},
{
"id": "b1",
"type": "button",
"text": "Register now",
"cssClass": "btn btn-primary",
"link": "t3://page?uid=42",
"placements": { "lg": { "x": 40, "y": 75, "w": 20, "h": 8, "rot": 0, "z": 4, "visible": true } }
}
]
}
Field notes:
type—"image"(default),"text"or"button".tag— HTML element of a text layer:p,h1–h6(whitelisted; absent =div).text— label (text/button layers).link— TYPO3 link (t3://…), works on any layer; a button is rendered as an<a>typolink.fit—fill|cover|contain(image layers).focusX/focusY— 0–100 %, →object-position(image layers).cssClass— space-separated, whitelisted predefined classes.overlay— 0–100 %, dark scrim behind text.locked— layer locked in the editor (not draggable).placements[<bp>]—x,y,w,h(%),rot(deg),z,visible; optionallyimage: {crop, focusX, focusY}— the breakpoint's own framing (art direction), overriding the layer'scrop/focusX/focusYthere.
tx_herobuilder_template holds the same composition JSON plus a title and an indicator
color; presets are shipped as JSON ({title, color, layers}).
Installation
composer require wapplersystems/herobuilder
vendor/bin/typo3 extension:setup # or activate in the Extension Manager
Then add a Hero Builder content element on any page and open its Collages tab.
Configuration
Stage aspect ratios (TypoScript constants)
Frontend stage ratio per breakpoint (W:H):
plugin.tx_herobuilder.settings.stages {
xs.ratio = 9:16
sm.ratio = 3:4
md.ratio = 16:9
lg.ratio = 21:9
xl.ratio = 21:9
xxl.ratio = 21:9
}
The backend editor additionally renders each breakpoint at a realistic reference device width (defaults: xs 390, sm 576, md 768, lg 992, xl 1200, xxl 1400 px).
Fixed stage height (page TSconfig + content element)
Per content element, Stage height (tt_content.stage_mode, tab Collages) switches between
Aspect ratio (default, above) and Fixed height. The heights are a site setting:
tx_herobuilder.stages {
xs.height = 43rem
lg.height = 55rem
}
Heights are inherited upwards (here: xs–md 43rem, lg and above 55rem); a breakpoint without a
height keeps its ratio. Only px and rem are accepted (rem converted at 16px), so the editor
can show the stage exactly as the frontend renders it at the reference width. The width stays
fluid in the frontend — layers keep their % positions, so between reference widths their
proportions shift (as with any fixed-height slider). The canvas follows a change of the field
immediately, before the record is saved.
Composition format version and links
The editor writes "version": 1 into the composition JSON (Composition::VERSION). JSON without
a version is version 1. When the shape changes incompatibly, raise the constant and add the step
to Composition::migrate() — stored collages and templates are then brought forward on read.
Links live on layers only: one link per layer, rendered as an <a> around that layer — never
nested. A link on a full-stage background layer makes the whole slide clickable while the
buttons on top keep their own links. (A separate "slide link" field existed earlier but was never
rendered; it is gone from the form, its database column is left in place.)
Image layers take their alternative text from the file's metadata; the editor shows it as the placeholder of the "Alternative text" field, and a text entered there overrides it for that layer only. The panel warns about a button without a link (not clickable) and a linked image without any alternative text (a link without a name for screen readers).
Predefined layer classes (page TSconfig)
Selectable per layer in the editor and used as the frontend whitelist. One list per layer type — the editor only offers a layer the classes of its type, and the frontend only renders a class on a layer of that type. Override or extend per site.
tx_herobuilder.layerClasses {
text {
display-1 = Display 1 (XXL)
fw-bold = Bold
text-bg-primary = Box: primary
# …
}
button {
btn = Button base
btn-primary = Button: primary
# …
}
image {
rounded-3 = Rounded
shadow = Shadow
}
# Entries directly under layerClasses (the original flat format) apply to every type.
}
The shipped set covers Bootstrap text utilities, btn/btn-* for buttons and a few image
utilities. A class stored on a layer that is not (or no longer) offered for its type shows as
"not available" in the editor and is not rendered. Class names may use any utility syntax
(md:text-lg, w-1/2); whitespace, quotes and <> are rejected.
Class groups in the editor (page TSconfig)
Optional presentation for the class chips — the whitelist stays layerClasses. Per layer type,
numbered groups with a label, a comma list of classes, exclusive = 1 (selecting one class
deselects the others of the group) and background = 1 (the classes set the layer background;
the text darkening is locked while one of them is selected). Offered classes in no group appear
under "More"; without any group configuration the chips stay one plain list.
tx_herobuilder.layerClassGroups.text {
10 {
label = Size
exclusive = 1
classes = display-1, display-3, fs-1, fs-5
}
50 {
label = Box
exclusive = 1
background = 1
classes = text-bg-primary, text-bg-dark
}
}
Frame and spacing (TypoScript constant)
The hero renders inside the theme's Default content layout — frame with anchor id, spacing,
container, header, background — like any other content element. In a theme's full-bleed hero
area that frame would box the hero in, so list those columns in a constant; there the hero
renders bare (layout HeroPlain):
# T3Bootstrap's hero area is colPos 5. Empty (default): every hero uses the Default layout.
plugin.tx_herobuilder.settings.framelessColPos = 5
New hero elements start with a hidden header (header_layout = 100), so their title does not
appear above the stage unless an editor chooses so.
Overriding the templates
The extension's template, partial and layout paths sit at index 1 of lib.contentElement's
templateRootPaths / partialRootPaths / layoutRootPaths — below the theme (T3Bootstrap: 5)
and the site's own paths (10). A site overrides Hero.html or the Slide / Layer partials
by providing them in its own path; its own Header/… partials and Default layout apply to the
hero as well.
The site's own paths come from styles.templates.templateRootPath / partialRootPath /
layoutRootPath. With site sets (TYPO3 13+) these are site settings of the Fluid Styled
Content set: set them in the site's settings.yaml, not as TypoScript constants — a site setting
wins over a constant of the same name, so a constant has no effect.
Template storage folder (page TSconfig)
Where “Save as template” stores tx_herobuilder_template records. Templates are global (listed
regardless of pid); point this at a sysfolder. Falls back to the record’s pid if unset.
tx_herobuilder.templatePid = 123
Image format (page TSconfig)
Image layer derivatives are written as WebP by default — several times smaller than PNG cut-outs, with their transparency kept. SVG and GIF sources are never converted.
tx_herobuilder.imageFormat = webp # default; "original" keeps the source format
The format must be listed in $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'] and writable
by the configured processor — otherwise the source format is used. (GraphicsMagick usually reads
AVIF but cannot write it.)
Live-preview stylesheets (page TSconfig)
CSS pulled into the live-preview iframe so it looks like the frontend. The extension always adds
its own hero.css/aos.css; add the site's compiled theme CSS here (absolute URLs used as-is,
EXT:/site-relative paths resolved and skipped if missing).
tx_herobuilder.previewCss {
10 = /typo3temp/assets/css/mytheme.css
20 = EXT:my_sitepackage/Resources/Public/Css/fonts.css
}
Keyboard shortcuts
Active while the stage/editor is focused.
| Shortcut | Action |
|---|---|
| Arrow keys | Nudge selected layer 1 % (Shift = 5 %) |
Delete / Backspace |
Delete selected layer |
Ctrl/⌘ + Z |
Undo |
Ctrl/⌘ + Y / Ctrl/⌘ + Shift + Z |
Redo |
Ctrl/⌘ + D |
Duplicate selected layer |
Ctrl/⌘ + + / − / 0 |
Zoom in / out / reset |
Ctrl/⌘ + wheel |
Zoom (anchored at cursor) |
Middle-mouse drag · Space + drag |
Pan the stage |
| Right-click | Context menu (align, fill stage, z-order, …) |
Architecture
Backend
Backend/Form/Element/CanvasElement— custom FormEngine nodeherobuilderCanvas(renderType), builds the toolbar/stage/panel and hands localized labels + config to the JS.Resources/Public/JavaScript/Backend/canvas.js— the Moveable-based editor (breakpoint tabs, layers, panel, layer list, snap, zoom/pan, undo/redo, context menu, live-preview driver).- AJAX routes:
herobuilder_fileinfo(FAL uids → URL/dimensions),herobuilder_preview(composition → HTML document for the iframe),herobuilder_template_list/herobuilder_template_save(templates),herobuilder_export(composition → social/OG image). Backend/Controller/PreviewController— renders the unsaved composition via the sharedCompositionProcessor::expand()+ thePreview.htmlpartial, CSP-safe (see above).Backend/Controller/TemplateController— lists presets +tx_herobuilder_templaterecords (with thumbnails) and saves new templates viaDataHandler.Backend/Controller/ExportController— renders a chosen social/OG format viaCompositeImageService.Service/CompositeImageService— flattens a composition to a cached PNG via ImageMagick (used for template thumbnails and export).Backend/Preview/HeroBuilderPreviewRenderer— page-module preview.- Records/config:
tx_herobuilder_template(table + TCA, allowed on standard pages), presets inResources/Private/Templates/Presets/*.json. - The templates gallery ships its styles inline (
GALLERY_CSSincanvas.js) because the TYPO3 modal renders in the top document, where the field stylesheet is not loaded.
Frontend
DataProcessing/CompositionProcessor— resolves FAL layers and generates the scoped per-breakpoint CSS (geometry,object-fit,object-position, text scrim, class whitelist) and the image layers'srcset/sizes.Domain/Composition— thecompositionvalue object (breakpoints, default stage ratios/widths, JSON parsing incl. text layers).- Templates:
Templates/Hero.html(carousel) →Partials/Slide.html→Partials/Layer.html;Partials/Preview.html(style-less variant for the CSP-safe live preview). - Assets:
Css/hero.css,Css/aos.css,JavaScript/hero.js(AOS init),JavaScript/preview-frame.js(in-iframe CSS injection + AOS replay).
Localization — Resources/Private/Language/locallang_db.xlf (en, source) and
de.locallang_db.xlf (German). JS strings are localized via a labels payload from PHP.