# VITEC ### Headless TYPO3 extension powering the VITEC website The editorial backend of a decoupled platform: TYPO3 v14 authors the content, this extension turns every content element into clean **JSON** for a React front end.
![TYPO3](https://img.shields.io/badge/TYPO3-14.3-FF8700?logo=typo3&logoColor=white) ![PHP](https://img.shields.io/badge/PHP-8.x-777BB4?logo=php&logoColor=white) ![Mode](https://img.shields.io/badge/mode-headless_JSON-0A3D62) ![Content Blocks](https://img.shields.io/badge/Content_Blocks-v2-2ea44f) ![License](https://img.shields.io/badge/license-GPL--2.0--or--later-blue) ![Status](https://img.shields.io/badge/status-active_development-yellow)
--- > **What is this?** > `evomedien/vitec` is the custom TYPO3 extension behind the VITEC relaunch. The site > runs **headless**: TYPO3 does not render HTML — it emits a JSON document per page that > a separate React front end consumes. This extension provides the domain models, > content elements, layout containers and the renderers that produce that JSON, plus > schema.org structured data for SEO. ## Table of contents - [Highlights](#highlights) - [Architecture at a glance](#architecture-at-a-glance) - [Content elements & plugins](#content-elements--plugins) - [Content Blocks](#content-blocks) - [Layout containers](#layout-containers) - [Structured data (JSON‑LD)](#structured-data-json-ld) - [Requirements](#requirements) - [Installation](#installation) - [Adding a new headless plugin](#adding-a-new-headless-plugin) - [Project structure](#project-structure) - [Documentation](#documentation) - [License](#license) ## Highlights - 🧩 **One JSON envelope for everything** — every content element, plugin, container and Content Block is exposed with the same predictable outer shape. - ⚡ **Pure JSON output** — domain payloads are built in PHP, fully decoupled from TypoScript and templates, so the logic is testable and versionable. - 🏗️ **Nestable by design** — plugins render both as top‑level page content and as children inside layout containers. - 🎛️ **Editor‑friendly Content Blocks** — hero, cards, CTA, FAQ, video, intro and a two‑column layout block, all with a unified header section. - 🔎 **SEO built in** — a schema.org `@graph` (Organization, Product, FAQ, Events, News …) is emitted per page. - 🛡️ **Fail‑soft** — a failing element yields empty output, never a broken page. ## Architecture at a glance ``` HTTP request (headless: 1) │ ▼ [L1] Page renderer friendsoftypo3/headless → { meta, content[], jsonLd } │ ▼ [L2] Content-element envelope lib.contentElement(WithHeader) → id, type, appearance, content{header…} │ ▼ [L3] Payload injection Classes/UserFunc/*JsonRenderer → content. (products, news, …) │ ▼ [L4] Layout containers vitec_cols_* = JSON → items[].contentElements[] │ ▼ [L5] Normalisation & nesting ContentElementResolver / ContainerChildrenProcessor │ ▼ [L6] Structured data PageJsonLdRenderer + StructuredDataService → @graph ``` Each renderer follows one pattern — an `#[AsAllowedCallable] render()` for top‑level use plus a `renderForRecord(array $row)` for reuse inside containers — and delegates serialisation to a service (see [`UsecaseSerializer`](Classes/Service/UsecaseSerializer.php) as the reference). > 📖 The full, normative architecture & interface specification lives in > **[`Documentation/Headless-JSON-Architecture.md`](Documentation/Headless-JSON-Architecture.md)**. ## Content elements & plugins | Domain | CType | Renderer | JSON key | |---|---|---|---| | Products | `vitec_productlist` / `vitec_productshow` | `ProductList/ProductShowJsonRenderer` | `products` / `product` | | Success Stories | `vitec_usecaselist` / `vitec_usecaseshow` | `UsecaseList/ShowJsonRenderer` → `UsecaseSerializer` | `usecases` / `usecase` | | Markets | `vitec_marketshow` | `MarketShowJsonRenderer` | `market` | | Solutions | `vitec_solutionshow` | `SolutionShowJsonRenderer` | `solution` | | Downloads | `vitec_downloadcard` / `vitec_downloadcardcollection` | `Downloadcard*JsonRenderer` | `downloadcard` / `downloadcardcollection` | | Datasheets | `vitec_datasheets` | `DatasheetsJsonRenderer` | `datasheets` | | Events | `vitec_eventlist` | `EventlistJsonRenderer` | `eventlist` | | News | `news_pi1` (+ variants) | `NewsJsonRenderer` | `news` | ## Content Blocks Declarative content elements (`friendsoftypo3/content-blocks`), serialised to JSON by `nb-headless-content-blocks`. All share the unified **header section**. | Block | Purpose | |---|---| | `hero-section` | Full‑width hero with background image/video, overlays and CTA | | `card` | Flexible card (image/icon, CTAs, many layout variants) | | `cta-banner` | Call‑to‑action banner | | `intro-paragraph` | Rich intro text with optional media | | `video` | YouTube or uploaded video with poster | | `faq` | Accordion; also feeds the `FAQPage` structured data | | `columns` | Two‑column layout (50/50 · 66/33 · 33/66) with per‑item content | ## Layout containers Nested column grids (`b13/container`) that own child content elements and emit them as `items`, with per‑column flex (`align`/`justify`) and a whole‑grid `gap`. | CType | Layout | |---|---| | `vitec_cols_50_50` | Two equal columns | | `vitec_cols_66_33` / `vitec_cols_33_66` | Asymmetric two columns | | `vitec_cols_33_33_33` | Three columns | | `vitec_cols_25_25_25_25` | Four columns | | `vitec_container` | Single column with a custom CSS class | | `vitec_cards_carousel` | Carousel of card elements | ## Structured data (JSON‑LD) `PageJsonLdRenderer` + `StructuredDataService` assemble a schema.org `@graph` per page: `Organization`, `WebSite` (root only), `BreadcrumbList`, `Product`, `VideoObject`, `FAQPage`, `ExhibitionEvent` and `NewsArticle`. ## Requirements | Component | Version | |---|---| | TYPO3 CMS | `^14.3` | | PHP | `8.x` | | `friendsoftypo3/headless` | `^5.0` | | `friendsoftypo3/content-blocks` | `^2.4` | | `netzbewegung/nb-headless-content-blocks` | `^0.0.23` | | `b13/container` | `^3.1` | | `georgringer/news` | `^14.0` | ## Installation ```bash composer require evomedien/vitec # apply database schema and clear caches vendor/bin/typo3 database:updateschema "*.add,*.change" vendor/bin/typo3 cache:flush ``` Enable headless mode in the site configuration (`config/sites//config.yaml`): ```yaml headless: 1 dependencies: - friendsoftypo3/headless - friendsoftypo3/headless-mixed - nb-headless-content-blocks/headless-content-blocks - georgringer/news ``` ## Adding a new headless plugin The short version (full normative rules in the architecture spec, Clause 9 & Annex A): 1. **Model / TCA / SQL** — create the domain table; declare table & field names as constants. 2. **Serializer** — `Classes/Service/Serializer` with `serializeListItem()` / `serializeDetail()`. 3. **Renderer** — `Classes/UserFunc/JsonRenderer` with `#[AsAllowedCallable] render()` + `renderForRecord()`, delegating to the serializer. 4. **TypoScript** — in `Configuration/Sets/Vitecset/setup.typoscript`: ```typoscript tt_content. < lib.contentElementWithHeader tt_content..fields.content.fields. = USER tt_content..fields.content.fields..userFunc = Evomedien\Vitec\UserFunc\->render ``` 5. **Nesting** — if it may sit inside a container, register it in `PLUGIN_RENDERERS` (in both `ContentElementResolver` and `ContainerChildrenProcessor`). 6. **Deploy** — `database:updateschema "*.add,*.change"` & `cache:flush`. > **Conventions:** list keys are plural, detail keys singular; page‑id via the > `frontend.page.information` request attribute; container CTypes derive with `<` (copy), > never `=<`. ## Project structure
packages/vitec/ — click to expand ``` packages/vitec/ ├── Classes/ │ ├── UserFunc/ # JSON renderers (one per plugin) — headless entry points │ ├── Service/ # Serializers, ContentElementResolver, StructuredDataService │ ├── DataProcessing/ # ContainerChildrenProcessor (container → items) │ ├── Domain/Model|Repository/ │ ├── Controller/ # Extbase controllers (non-headless / backend) │ └── Backend/ · View/ · Hook/ · EventListener/ ├── ContentBlocks/ │ └── ContentElements/ # card, cta-banner, columns, faq, hero-section, intro-paragraph, video ├── Configuration/ │ ├── Sets/Vitecset/ # setup.typoscript — the single headless entry point │ ├── TypoScript/Headless/ # containers, menus, news JSON definitions │ ├── TCA/ · FlexForms/ · Services.yaml ├── Documentation/ │ ├── Headless-JSON-Architecture.md # normative spec (start here) │ └── HeadlessIntegration.md # informal how-to ├── Resources/ └── ext_tables.sql · ext_localconf.php · composer.json ```
## Documentation | Document | Purpose | |---|---| | [`Documentation/Headless-JSON-Architecture.md`](Documentation/Headless-JSON-Architecture.md) | **Authoritative** architecture & JSON interface specification (ISO‑style) | | [`Documentation/HeadlessIntegration.md`](Documentation/HeadlessIntegration.md) | Informal step‑by‑step how‑to | ## License [GPL‑2.0‑or‑later](https://www.gnu.org/licenses/gpl-2.0.html) — © evomedien.
Built for the VITEC relaunch · TYPO3 headless + React