@ta-interaktiv/react-header-injection
v3.0.0
Published
Synchronously injects React content into Tamedia article headers with configurable title layouts.
Downloads
102
Readme
@ta-interaktiv/react-header-injection
Injects React content immediately before a Tamedia article title and applies the header layouts shared by the package's 23 current consumers.
Installation
bun add @ta-interaktiv/react-header-injection@^3.0.0react and react-dom 18 or newer are peer dependencies. The package has no
runtime dependencies.
HeaderInjection
content is required:
import {
HeaderImage,
HeaderInjection
} from '@ta-interaktiv/react-header-injection'
export function Header() {
return (
<HeaderInjection
content={
<HeaderImage
desktop="https://example.com/header-desktop.jpg"
mobile="https://example.com/header-mobile.jpg"
alt="Bicycle traffic in Zurich"
/>
}
/>
)
}Props:
| Prop | Type | Default |
|---|---|---|
| content | ReactNode | required |
| layout | 'default' \| 'centered' \| 'stacked' | 'default' |
| fallback | 'none' \| 'inline' | 'none' |
| styles | HeaderInjectionStyles | — |
Migrating from 2.x
firstSpan and titleFirstSpan are gone. Both selected the first span they
found, which on a real article header is the Abo badge rather than the
kicker, and both resolved to that same element.
| 2.x | 3.0 |
|---|---|
| firstSpan | titleHeader |
| titleFirstSpan | titleHeader for widths and layout, titleHeaderSpan for text |
The layout default display: inline-block moved from firstSpan to
titleHeader, so it now lands on .ArticleTitleHeader instead of the badge.
Layouts
defaultspans the injected container across the grid and centers the title.centeredadditionally centers the injected content.stackeduses a flex injection container and stacks the centered title vertically.
Style slots
styles supports container, title, titleHeader, and titleHeaderSpan.
Overrides merge over the selected layout:
<HeaderInjection
layout="stacked"
styles={{
container: { width: '100%' },
title: { marginTop: '1em' },
titleHeader: { minWidth: '80%' },
titleHeaderSpan: { color: '#333' }
}}
content={<MyHeader />}
/>The slots target:
container: the injected#inject-into-header-contentelement.title: the first articleh2.titleHeader:.ArticleTitleHeaderinside theh2— the wrapper holding the kicker, theAbobadge, and the hidden dash.titleHeaderSpan:span.titleheaderinside theh2— the kicker text only.
Every slot is selected by class or tag, never by position. The article header
contains a varying number of spans (span.premium exists only on paywalled
articles, and the kicker is absent when no title header is set), so an index
would resolve to a different element from one article to the next.
Use titleHeader for layout and titleHeaderSpan for text. The h2 is a
flex container whose items are .ArticleTitleHeader and the title text, so a
width that should push the title onto its own line has to be set on
titleHeader; the kicker span is not a flex item. Colours, weights, and sizes
belong on titleHeaderSpan.
Both slots no-op when their element is missing. Injection still succeeds and the fallback is not triggered.
A slot set to null disables all styles for that slot. A property explicitly
set to undefined removes that one property from the selected layout while
preserving its other styles:
styles={{
titleHeader: null,
title: { flexWrap: undefined }
}}Synchronous injection and fallback
On mount, the component immediately finds the first h2, resolves its closest
parent div — .ContentHead in the current article template — and inserts the
portal container before that div. The article DOM must already contain this
structure when the layout effect runs.
If the target is unavailable or injection fails, fallback is immediate:
nonerenders nothing.inlinerenderscontentin a plaindivat the component's original location.
HeaderImage
HeaderImage renders a responsive picture. Its mobile breakpoint is fixed at
768px (max-width: 767px).
| Prop | Type | Default |
|---|---|---|
| desktop | string | required |
| mobile | string | desktop |
| darkDesktop | string | desktop |
| darkMobile | string | mobile, then the selected desktop source |
| colorMode | 'light' \| 'dark' | 'light' |
| alt | string | required |
| imageStyles | CSSProperties | — |
<HeaderImage
desktop="/header-light-desktop.jpg"
mobile="/header-light-mobile.jpg"
darkDesktop="/header-dark-desktop.jpg"
darkMobile="/header-dark-mobile.jpg"
colorMode="dark"
alt="Mountain landscape"
imageStyles={{ display: 'block', width: '100%' }}
/>Custom content
content accepts any React node. Keep project-specific videos, animations,
charts, parallax pictures, and SVG in the consumer:
<HeaderInjection
layout="centered"
fallback="inline"
content={<video autoPlay muted loop playsInline src="/header.mp4" />}
/><HeaderInjection
content={
<svg viewBox="0 0 100 20" role="img" aria-label="Timeline">
<path d="M0 10H100" stroke="currentColor" />
</svg>
}
/>Consumer corpus
The reviewed corpus contains these 23 package consumers:
- Default:
interaktiv-2022-kaffeerechner,interaktiv-2024-srg-halbierungsintiative,interaktiv-2025-300-reichste,interaktiv-2025-chat-gpt-dialog,interaktiv-2025-fragen-zu-kopfschmerzen,interaktiv-2025-fragen-zum-schlaf,interaktiv-2025-notaufnahme-quiz,interaktiv-2025-rueckenschmerzen,interaktiv-2025-schaad-im-tschad,interaktiv-2025-superforecaster-quiz,interaktiv-2026-alkoholrechner,interaktiv-2026-jobs,interaktiv-2026-kirschbluete,interaktiv-2026-sirenen-quiz, andinteraktiv-2026-so-alt-sind-ihre-nachbarn. - Centered:
interaktiv-2025-cancer,interaktiv-2025-wine-quiz-battle,interaktiv-2026-airtravel-quiz, andinteraktiv-2026-political-ads. - Stacked with inline fallback:
interaktiv-2025-3D-lakes,interaktiv-2026-immobilienpreise,interaktiv-2026-velounfaelle-verortung, andinteraktiv-2026-velounfaelle.
The exact layout, fallback, and style overrides are recorded in
__tests__/fixtures/migration-props.json and covered by the parity suite.
Test and build
Run from the monorepo root:
yarn jest --config packages/react-header-injection/jest.config.cjs --runInBand
yarn workspace @ta-interaktiv/react-header-injection prepare
yarn workspace @ta-interaktiv/react-header-injection verify:package