diff --git a/.changeset/content-header-responsive-text.md b/.changeset/content-header-responsive-text.md
new file mode 100644
index 00000000000..377d6789638
--- /dev/null
+++ b/.changeset/content-header-responsive-text.md
@@ -0,0 +1,8 @@
+---
+'@siemens/ix': minor
+'@siemens/ix-angular': minor
+'@siemens/ix-react': minor
+'@siemens/ix-vue': minor
+---
+
+Add responsive wrapping for `ix-content-header` titles and subtitles and a `textOverflow="truncate"` option for compact single-line ellipsis behavior with native tooltips for both values.
diff --git a/packages/angular-standalone-test-app/src/app/app.routes.ts b/packages/angular-standalone-test-app/src/app/app.routes.ts
index 8e4893a92a7..df895ef48f9 100644
--- a/packages/angular-standalone-test-app/src/app/app.routes.ts
+++ b/packages/angular-standalone-test-app/src/app/app.routes.ts
@@ -128,6 +128,10 @@ export const routePaths: IxPreviewRoutes = {
import('../preview-examples/content-header').then((m) => m.default),
'preview/content-header-no-back': () =>
import('../preview-examples/content-header-no-back').then((m) => m.default),
+ 'preview/content-header-truncate': () =>
+ import('../preview-examples/content-header-truncate').then(
+ (m) => m.default
+ ),
'preview/content-header-with-slot': () =>
import('../preview-examples/content-header-with-slot').then(
(m) => m.default
diff --git a/packages/angular-standalone-test-app/src/preview-examples/content-header-truncate.html b/packages/angular-standalone-test-app/src/preview-examples/content-header-truncate.html
new file mode 100644
index 00000000000..69744c3863e
--- /dev/null
+++ b/packages/angular-standalone-test-app/src/preview-examples/content-header-truncate.html
@@ -0,0 +1,19 @@
+
+
+
+ Button1
+ Button2
+ Button3
+
diff --git a/packages/angular-standalone-test-app/src/preview-examples/content-header-truncate.ts b/packages/angular-standalone-test-app/src/preview-examples/content-header-truncate.ts
new file mode 100644
index 00000000000..f620d7861e0
--- /dev/null
+++ b/packages/angular-standalone-test-app/src/preview-examples/content-header-truncate.ts
@@ -0,0 +1,18 @@
+/*
+ * SPDX-FileCopyrightText: 2026 Siemens AG
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { Component } from '@angular/core';
+import { IxButton, IxContentHeader } from '@siemens/ix-angular/standalone';
+
+@Component({
+ selector: 'app-example',
+ imports: [IxContentHeader, IxButton],
+ templateUrl: './content-header-truncate.html',
+})
+export default class ContentHeaderTruncate {}
diff --git a/packages/angular-standalone-test-app/src/preview-examples/content-header.html b/packages/angular-standalone-test-app/src/preview-examples/content-header.html
index 9dc2c413a9f..8d026c8db04 100644
--- a/packages/angular-standalone-test-app/src/preview-examples/content-header.html
+++ b/packages/angular-standalone-test-app/src/preview-examples/content-header.html
@@ -9,8 +9,8 @@
Button1
Button2
diff --git a/packages/angular-test-app/src/app/app-routing.module.ts b/packages/angular-test-app/src/app/app-routing.module.ts
index b3de23a66cd..b19a3f17372 100644
--- a/packages/angular-test-app/src/app/app-routing.module.ts
+++ b/packages/angular-test-app/src/app/app-routing.module.ts
@@ -62,6 +62,7 @@ import Chip from '../preview-examples/chip';
import ContentExample from '../preview-examples/content';
import ContentHeader from '../preview-examples/content-header';
import ContentHeaderNoBack from '../preview-examples/content-header-no-back';
+import ContentHeaderTruncate from '../preview-examples/content-header-truncate';
import ContentHeaderWithSlot from '../preview-examples/content-header-with-slot';
import CustomField from '../preview-examples/custom-field';
import CustomFieldValidation from '../preview-examples/custom-field-validation';
@@ -547,6 +548,10 @@ const routes: Routes = [
path: 'content-header',
component: ContentHeader,
},
+ {
+ path: 'content-header-truncate',
+ component: ContentHeaderTruncate,
+ },
{
path: 'content-header-with-slot',
component: ContentHeaderWithSlot,
diff --git a/packages/angular-test-app/src/app/app.module.ts b/packages/angular-test-app/src/app/app.module.ts
index 600cb855076..f68db33af8b 100644
--- a/packages/angular-test-app/src/app/app.module.ts
+++ b/packages/angular-test-app/src/app/app.module.ts
@@ -71,6 +71,7 @@ import Chip from '../preview-examples/chip';
import ContentExample from '../preview-examples/content';
import ContentHeader from '../preview-examples/content-header';
import ContentHeaderNoBack from '../preview-examples/content-header-no-back';
+import ContentHeaderTruncate from '../preview-examples/content-header-truncate';
import ContentHeaderWithSlot from '../preview-examples/content-header-with-slot';
import CustomField from '../preview-examples/custom-field';
import CustomFieldValidation from '../preview-examples/custom-field-validation';
@@ -348,6 +349,7 @@ import WorkflowVertical from '../preview-examples/workflow-vertical';
ContentExample,
ContentHeader,
ContentHeaderNoBack,
+ ContentHeaderTruncate,
ContentHeaderWithSlot,
DatepickerRange,
Datepicker,
diff --git a/packages/angular-test-app/src/preview-examples/content-header-truncate.html b/packages/angular-test-app/src/preview-examples/content-header-truncate.html
new file mode 100644
index 00000000000..69744c3863e
--- /dev/null
+++ b/packages/angular-test-app/src/preview-examples/content-header-truncate.html
@@ -0,0 +1,19 @@
+
+
+
+ Button1
+ Button2
+ Button3
+
diff --git a/packages/angular-test-app/src/preview-examples/content-header-truncate.ts b/packages/angular-test-app/src/preview-examples/content-header-truncate.ts
new file mode 100644
index 00000000000..f513eb77e95
--- /dev/null
+++ b/packages/angular-test-app/src/preview-examples/content-header-truncate.ts
@@ -0,0 +1,17 @@
+/*
+ * SPDX-FileCopyrightText: 2026 Siemens AG
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { Component } from '@angular/core';
+
+@Component({
+ standalone: false,
+ selector: 'app-example',
+ templateUrl: './content-header-truncate.html',
+})
+export default class ContentHeaderTruncate {}
diff --git a/packages/angular-test-app/src/preview-examples/content-header.html b/packages/angular-test-app/src/preview-examples/content-header.html
index 99504615115..17ee9f49fe8 100644
--- a/packages/angular-test-app/src/preview-examples/content-header.html
+++ b/packages/angular-test-app/src/preview-examples/content-header.html
@@ -9,8 +9,8 @@
Button1
Button2
diff --git a/packages/angular/src/components.ts b/packages/angular/src/components.ts
index 07d38cb729d..13cb925740f 100644
--- a/packages/angular/src/components.ts
+++ b/packages/angular/src/components.ts
@@ -712,14 +712,14 @@ export declare interface IxContent extends Components.IxContent {}
@ProxyCmp({
- inputs: ['hasBackButton', 'headerSubtitle', 'headerTitle', 'variant']
+ inputs: ['hasBackButton', 'headerSubtitle', 'headerTitle', 'textOverflow', 'variant']
})
@Component({
selector: 'ix-content-header',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
- inputs: ['hasBackButton', 'headerSubtitle', 'headerTitle', 'variant'],
+ inputs: ['hasBackButton', 'headerSubtitle', 'headerTitle', 'textOverflow', 'variant'],
outputs: ['backButtonClick'],
standalone: false
})
diff --git a/packages/angular/standalone/src/components.ts b/packages/angular/standalone/src/components.ts
index 6c406a74416..023358b0b60 100644
--- a/packages/angular/standalone/src/components.ts
+++ b/packages/angular/standalone/src/components.ts
@@ -823,14 +823,14 @@ export declare interface IxContent extends Components.IxContent {}
@ProxyCmp({
defineCustomElementFn: defineIxContentHeader,
- inputs: ['hasBackButton', 'headerSubtitle', 'headerTitle', 'variant']
+ inputs: ['hasBackButton', 'headerSubtitle', 'headerTitle', 'textOverflow', 'variant']
})
@Component({
selector: 'ix-content-header',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
- inputs: ['hasBackButton', 'headerSubtitle', 'headerTitle', 'variant'],
+ inputs: ['hasBackButton', 'headerSubtitle', 'headerTitle', 'textOverflow', 'variant'],
outputs: ['backButtonClick'],
})
export class IxContentHeader {
diff --git a/packages/core/src/components.d.ts b/packages/core/src/components.d.ts
index c555e9915ab..55e693e61d7 100644
--- a/packages/core/src/components.d.ts
+++ b/packages/core/src/components.d.ts
@@ -22,7 +22,7 @@ import { InputState } from "./components/category-filter/input-state";
import { ChatAttachmentStatus } from "./components/chat-attachment/chat-attachment.types";
import { ChipVariant } from "./components/chip/chip.types";
import { ColumnSize } from "./components/col/col.types";
-import { ContentHeaderVariant } from "./components/content-header/content-header.types";
+import { ContentHeaderTextOverflow, ContentHeaderVariant } from "./components/content-header/content-header.types";
import { CssGridTemplateType } from "./components/css-grid/css-grid.types";
import { ButtonVariant as ButtonVariant1 } from "./components/button/button";
import { DateDropdownOption, DateRangeChangeEvent } from "./components/date-dropdown/date-dropdown.types";
@@ -79,7 +79,7 @@ export { InputState } from "./components/category-filter/input-state";
export { ChatAttachmentStatus } from "./components/chat-attachment/chat-attachment.types";
export { ChipVariant } from "./components/chip/chip.types";
export { ColumnSize } from "./components/col/col.types";
-export { ContentHeaderVariant } from "./components/content-header/content-header.types";
+export { ContentHeaderTextOverflow, ContentHeaderVariant } from "./components/content-header/content-header.types";
export { CssGridTemplateType } from "./components/css-grid/css-grid.types";
export { ButtonVariant as ButtonVariant1 } from "./components/button/button";
export { DateDropdownOption, DateRangeChangeEvent } from "./components/date-dropdown/date-dropdown.types";
@@ -1051,6 +1051,12 @@ export namespace Components {
* Title of Header
*/
"headerTitle"?: string;
+ /**
+ * Controls how the title and subtitle handle limited horizontal space.
+ * @since 5.2.0
+ * @default 'wrap'
+ */
+ "textOverflow": ContentHeaderTextOverflow;
/**
* Variant of content header
* @default 'primary'
@@ -8020,6 +8026,12 @@ declare namespace LocalJSX {
* Triggered when back button is clicked
*/
"onBackButtonClick"?: (event: IxContentHeaderCustomEvent) => void;
+ /**
+ * Controls how the title and subtitle handle limited horizontal space.
+ * @since 5.2.0
+ * @default 'wrap'
+ */
+ "textOverflow"?: ContentHeaderTextOverflow;
/**
* Variant of content header
* @default 'primary'
@@ -12383,6 +12395,7 @@ declare namespace LocalJSX {
"variant": ContentHeaderVariant;
"headerTitle": string;
"headerSubtitle": string | undefined;
+ "textOverflow": ContentHeaderTextOverflow;
"hasBackButton": boolean;
}
interface IxCssGridItemAttributes {
diff --git a/packages/core/src/components/content-header/content-header.scss b/packages/core/src/components/content-header/content-header.scss
index b973297e24f..ab93d598df5 100644
--- a/packages/core/src/components/content-header/content-header.scss
+++ b/packages/core/src/components/content-header/content-header.scss
@@ -18,19 +18,30 @@
flex-direction: column;
flex: 1 1 0;
min-width: 0;
- white-space: nowrap;
margin-right: 0.5rem;
- .titleOverflow {
- overflow: hidden;
- text-overflow: ellipsis;
+ .headerText {
+ min-width: 0;
+ overflow-wrap: anywhere;
+
+ &.truncate {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
}
.headerTitleRow {
display: flex;
+ .headerText {
+ flex: 0 1 auto;
+ }
+
.headerSlot {
display: inline-flex;
+ flex: 0 0 auto;
+ align-self: flex-start;
margin-left: 0.5rem;
}
}
diff --git a/packages/core/src/components/content-header/content-header.tsx b/packages/core/src/components/content-header/content-header.tsx
index ef271ac3f9e..d27c31f0a71 100644
--- a/packages/core/src/components/content-header/content-header.tsx
+++ b/packages/core/src/components/content-header/content-header.tsx
@@ -9,7 +9,10 @@
import { iconArrowLeft } from '@siemens/ix-icons/icons';
import { Component, Event, EventEmitter, h, Host, Prop } from '@stencil/core';
-import type { ContentHeaderVariant } from './content-header.types';
+import type {
+ ContentHeaderTextOverflow,
+ ContentHeaderVariant,
+} from './content-header.types';
/**
* @slot header - Content to be placed in the header area next to the title
@@ -36,6 +39,13 @@ export class ContentHeader {
*/
@Prop() headerSubtitle: string | undefined = undefined;
+ /**
+ * Controls how the title and subtitle handle limited horizontal space.
+ *
+ * @since 5.2.0
+ */
+ @Prop({ reflect: true }) textOverflow: ContentHeaderTextOverflow = 'wrap';
+
/**
* Display a back button
*/
@@ -64,8 +74,12 @@ export class ContentHeader {
format={this.variant === 'secondary' ? 'h4' : 'h3'}
class={{
secondary: this.variant === 'secondary',
- titleOverflow: true,
+ headerText: true,
+ truncate: this.textOverflow === 'truncate',
}}
+ title={
+ this.textOverflow === 'truncate' ? this.headerTitle : undefined
+ }
>
{this.headerTitle}
@@ -79,9 +93,14 @@ export class ContentHeader {
text-color={'soft'}
class={{
subtitle: this.variant === 'secondary',
- titleOverflow: true,
+ headerText: true,
+ truncate: this.textOverflow === 'truncate',
}}
- title={this.headerSubtitle}
+ title={
+ this.textOverflow === 'truncate'
+ ? this.headerSubtitle
+ : undefined
+ }
>
{this.headerSubtitle}
diff --git a/packages/core/src/components/content-header/content-header.types.ts b/packages/core/src/components/content-header/content-header.types.ts
index 942be25721c..fb4d835f700 100644
--- a/packages/core/src/components/content-header/content-header.types.ts
+++ b/packages/core/src/components/content-header/content-header.types.ts
@@ -7,3 +7,5 @@
* LICENSE file in the root directory of this source tree.
*/
export type ContentHeaderVariant = 'primary' | 'secondary';
+
+export type ContentHeaderTextOverflow = 'wrap' | 'truncate';
diff --git a/packages/core/src/components/content-header/test/content-header.ct.ts b/packages/core/src/components/content-header/test/content-header.ct.ts
new file mode 100644
index 00000000000..ec907d35340
--- /dev/null
+++ b/packages/core/src/components/content-header/test/content-header.ct.ts
@@ -0,0 +1,281 @@
+/*
+ * SPDX-FileCopyrightText: 2026 Siemens AG
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+import { expect, type Locator } from '@playwright/test';
+import { regressionTest, viewPorts } from '@utils/test';
+
+const getTextLayout = (text: Locator) =>
+ text.evaluate((element) => {
+ const style = getComputedStyle(element);
+
+ return {
+ clientWidth: element.clientWidth,
+ height: element.getBoundingClientRect().height,
+ lineHeight: Number.parseFloat(style.lineHeight),
+ overflow: style.overflow,
+ scrollWidth: element.scrollWidth,
+ textOverflow: style.textOverflow,
+ whiteSpace: style.whiteSpace,
+ };
+ });
+
+regressionTest('accessibility', async ({ mount, makeAxeBuilder }) => {
+ await mount(`
+
+ Online
+ Edit
+
+ `);
+
+ const results = await makeAxeBuilder().analyze();
+ expect(results.violations).toEqual([]);
+});
+
+regressionTest('renders', async ({ mount, page }) => {
+ await mount(`
+
+ `);
+
+ const header = page.locator('ix-content-header');
+ await expect(header).toHaveClass(/\bhydrated\b/);
+ await expect(header).toHaveAttribute('text-overflow', 'wrap');
+ await expect(header).toHaveJSProperty('textOverflow', 'wrap');
+ await expect(page.getByText('Content title', { exact: true })).toBeVisible();
+ await expect(page.getByText('Subtitle', { exact: true })).toBeVisible();
+});
+
+regressionTest(
+ 'wraps title and subtitle within the available component width by default',
+ async ({ mount, page }) => {
+ const title =
+ 'A responsive content header title that remains completely visible';
+ const subtitle =
+ 'Supporting context also wraps naturally in constrained containers';
+
+ await mount(`
+
+ Header action
+
+ `);
+
+ const header = page.locator('ix-content-header');
+ await expect(header).toHaveClass(/\bhydrated\b/);
+ const titleText = header.getByText(title, { exact: true });
+ const subtitleText = header.getByText(subtitle, { exact: true });
+
+ for (const text of [titleText, subtitleText]) {
+ const layout = await getTextLayout(text);
+
+ expect(layout.height).toBeGreaterThan(layout.lineHeight);
+ expect(layout.scrollWidth).toBeLessThanOrEqual(layout.clientWidth);
+ await expect(text).not.toHaveAttribute('title');
+ }
+ }
+);
+
+regressionTest(
+ 'wraps at the small viewport and remains on one line at larger viewports',
+ async ({ mount, page }) => {
+ const title =
+ 'Responsive content header text uses the space available to the component';
+
+ await page.setViewportSize(viewPorts.sm);
+ await mount(
+ ``
+ );
+
+ const header = page.locator('ix-content-header');
+ await expect(header).toHaveClass(/\bhydrated\b/);
+ const titleText = header.getByText(title, { exact: true });
+ const getLineCount = async () => {
+ const layout = await getTextLayout(titleText);
+ return layout.height / layout.lineHeight;
+ };
+
+ expect(await getLineCount()).toBeGreaterThan(1);
+
+ await page.setViewportSize(viewPorts.md);
+ expect(await getLineCount()).toBe(1);
+
+ await page.setViewportSize(viewPorts.lg);
+ expect(await getLineCount()).toBe(1);
+ }
+);
+
+regressionTest(
+ 'breaks long unbroken text without horizontal overflow',
+ async ({ mount, page }) => {
+ const title =
+ 'production-line-identifier-without-natural-break-opportunities-123456789';
+
+ await mount(`
+
+ `);
+
+ const header = page.locator('ix-content-header');
+ await expect(header).toHaveClass(/\bhydrated\b/);
+ const titleText = header.getByText(title, { exact: true });
+ const layout = await getTextLayout(titleText);
+
+ expect(layout.height).toBeGreaterThan(layout.lineHeight);
+ expect(layout.scrollWidth).toBeLessThanOrEqual(layout.clientWidth);
+ }
+);
+
+regressionTest(
+ 'keeps header content and actions aligned with the first title line',
+ async ({ mount, page }) => {
+ const title =
+ 'A constrained title that wraps while adjacent content keeps its place';
+ const subtitle = 'Supporting context stays clear of the action area';
+
+ await mount(`
+
+ Online
+ Non-wrapping action
+
+ `);
+
+ const header = page.locator('ix-content-header');
+ await expect(header).toHaveClass(/\bhydrated\b/);
+ const titleText = header.getByText(title, { exact: true });
+ const subtitleText = header.getByText(subtitle, { exact: true });
+ const pill = header.getByText('Online', { exact: true });
+ const action = page.getByRole('button', { name: 'Non-wrapping action' });
+ const backButton = page.getByRole('button', { name: 'Arrow Left' });
+
+ const titleBox = await titleText.boundingBox();
+ const subtitleBox = await subtitleText.boundingBox();
+ const pillBox = await pill.boundingBox();
+ const actionBox = await action.boundingBox();
+ const backButtonBox = await backButton.boundingBox();
+ const titleLineHeight = await titleText.evaluate((element) =>
+ Number.parseFloat(getComputedStyle(element).lineHeight)
+ );
+
+ expect(titleBox).not.toBeNull();
+ expect(subtitleBox).not.toBeNull();
+ expect(pillBox).not.toBeNull();
+ expect(actionBox).not.toBeNull();
+ expect(backButtonBox).not.toBeNull();
+ expect(titleBox!.height).toBeGreaterThan(titleLineHeight);
+ expect(pillBox!.y).toBeLessThan(titleBox!.y + titleLineHeight);
+ expect(actionBox!.y).toBeLessThan(titleBox!.y + titleLineHeight);
+ expect(backButtonBox!.y).toBeLessThan(titleBox!.y + titleLineHeight);
+ expect(titleBox!.x + titleBox!.width).toBeLessThanOrEqual(pillBox!.x);
+ expect(subtitleBox!.x + subtitleBox!.width).toBeLessThanOrEqual(
+ actionBox!.x
+ );
+
+ await header.evaluate((element) => {
+ element.style.width = '80rem';
+ });
+ const wideActionBox = await action.boundingBox();
+ expect(wideActionBox).not.toBeNull();
+ expect(actionBox!.height).toBe(wideActionBox!.height);
+ expect(actionBox!.width).toBe(wideActionBox!.width);
+ }
+);
+
+regressionTest(
+ 'truncates title and subtitle on separate lines with complete native tooltips',
+ async ({ mount, page }) => {
+ const title = 'A long title that is truncated in compact layouts';
+ const subtitle = 'A long subtitle that is truncated independently';
+
+ await mount(`
+
+ `);
+
+ const header = page.locator('ix-content-header');
+ await expect(header).toHaveClass(/\bhydrated\b/);
+ const titleText = header.getByText(title, { exact: true });
+ const subtitleText = header.getByText(subtitle, { exact: true });
+
+ for (const [text, completeValue] of [
+ [titleText, title],
+ [subtitleText, subtitle],
+ ] as const) {
+ const overflow = await getTextLayout(text);
+
+ expect(overflow.height).toBe(overflow.lineHeight);
+ expect(overflow.scrollWidth).toBeGreaterThan(overflow.clientWidth);
+ expect(overflow.overflow).toBe('hidden');
+ expect(overflow.textOverflow).toBe('ellipsis');
+ expect(overflow.whiteSpace).toBe('nowrap');
+ await expect(text).toHaveAttribute('title', completeValue);
+ }
+
+ const titleBox = await titleText.boundingBox();
+ const subtitleBox = await subtitleText.boundingBox();
+ expect(titleBox).not.toBeNull();
+ expect(subtitleBox).not.toBeNull();
+ expect(titleBox!.y + titleBox!.height).toBeLessThanOrEqual(subtitleBox!.y);
+ }
+);
+
+regressionTest(
+ 'updates overflow behavior when textOverflow changes at runtime',
+ async ({ mount, page }) => {
+ const title = 'A title that switches between wrapping and truncation';
+
+ await mount(`
+
+ `);
+
+ const header = page.locator('ix-content-header');
+ await expect(header).toHaveClass(/\bhydrated\b/);
+ const titleText = header.getByText(title, { exact: true });
+ const getHeight = () =>
+ titleText.evaluate((element) => element.getBoundingClientRect().height);
+
+ const wrappedHeight = await getHeight();
+
+ await header.evaluate((element: HTMLIxContentHeaderElement) => {
+ element.textOverflow = 'truncate';
+ });
+
+ await expect(header).toHaveAttribute('text-overflow', 'truncate');
+ await expect(titleText).toHaveAttribute('title', title);
+ await expect.poll(getHeight).toBeLessThan(wrappedHeight);
+
+ await header.evaluate((element: HTMLIxContentHeaderElement) => {
+ element.textOverflow = 'wrap';
+ });
+
+ await expect(header).toHaveAttribute('text-overflow', 'wrap');
+ await expect(titleText).not.toHaveAttribute('title');
+ await expect.poll(getHeight).toBe(wrappedHeight);
+ }
+);
diff --git a/packages/html-test-app/src/preview-examples/content-header-truncate.html b/packages/html-test-app/src/preview-examples/content-header-truncate.html
new file mode 100644
index 00000000000..c3b0500d288
--- /dev/null
+++ b/packages/html-test-app/src/preview-examples/content-header-truncate.html
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+ Content header truncation example
+
+
+
+ Button1
+ Button2
+ Button3
+
+
+
+
diff --git a/packages/html-test-app/src/preview-examples/content-header.html b/packages/html-test-app/src/preview-examples/content-header.html
index 5eecdaf7146..85da75f6410 100644
--- a/packages/html-test-app/src/preview-examples/content-header.html
+++ b/packages/html-test-app/src/preview-examples/content-header.html
@@ -17,8 +17,8 @@
Button1
Button2
diff --git a/packages/react-test-app/src/main.tsx b/packages/react-test-app/src/main.tsx
index e1da8f0e39d..5aa0f686812 100644
--- a/packages/react-test-app/src/main.tsx
+++ b/packages/react-test-app/src/main.tsx
@@ -67,6 +67,7 @@ import Chip from './preview-examples/chip';
import Content from './preview-examples/content';
import ContentHeader from './preview-examples/content-header';
import ContentHeaderNoBack from './preview-examples/content-header-no-back';
+import ContentHeaderTruncate from './preview-examples/content-header-truncate';
import ContentHeaderWithSlot from './preview-examples/content-header-with-slot';
import CustomField from './preview-examples/custom-field';
import CustomFieldValidation from './preview-examples/custom-field-validation';
@@ -341,6 +342,7 @@ const routes: IxPreviewRoutes = {
'/preview/datetime-range': DatetimeRange,
'/preview/content-header-no-back': ContentHeaderNoBack,
'/preview/content-header': ContentHeader,
+ '/preview/content-header-truncate': ContentHeaderTruncate,
'/preview/content-header-with-slot': ContentHeaderWithSlot,
'/preview/datepicker-range': DatepickerRange,
'/preview/datepicker': Datepicker,
diff --git a/packages/react-test-app/src/preview-examples/content-header-truncate.tsx b/packages/react-test-app/src/preview-examples/content-header-truncate.tsx
new file mode 100644
index 00000000000..bfce96b05d6
--- /dev/null
+++ b/packages/react-test-app/src/preview-examples/content-header-truncate.tsx
@@ -0,0 +1,25 @@
+/*
+ * SPDX-FileCopyrightText: 2026 Siemens AG
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import { IxButton, IxContentHeader } from '@siemens/ix-react';
+
+export default () => {
+ return (
+
+ Button1
+ Button2
+ Button3
+
+ );
+};
diff --git a/packages/react-test-app/src/preview-examples/content-header.tsx b/packages/react-test-app/src/preview-examples/content-header.tsx
index 7da1b544556..4a51bfb16b5 100644
--- a/packages/react-test-app/src/preview-examples/content-header.tsx
+++ b/packages/react-test-app/src/preview-examples/content-header.tsx
@@ -12,9 +12,9 @@ import { IxButton, IxContentHeader } from '@siemens/ix-react';
export default () => {
return (
Button1
Button2
diff --git a/packages/react/src/components/components.server.ts b/packages/react/src/components/components.server.ts
index f348760a3fa..cabd8f1c721 100644
--- a/packages/react/src/components/components.server.ts
+++ b/packages/react/src/components/components.server.ts
@@ -590,6 +590,7 @@ export const IxContentHeader: StencilReactComponent) : undefined,
diff --git a/packages/vue-test-app/src/Root.vue b/packages/vue-test-app/src/Root.vue
index a6350968be0..5a12c179489 100644
--- a/packages/vue-test-app/src/Root.vue
+++ b/packages/vue-test-app/src/Root.vue
@@ -64,6 +64,7 @@ import Checkbox from './preview-examples/checkbox.vue';
import Chip from './preview-examples/chip.vue';
import ContentHeaderNoBack from './preview-examples/content-header-no-back.vue';
import ContentHeader from './preview-examples/content-header.vue';
+import ContentHeaderTruncate from './preview-examples/content-header-truncate.vue';
import ContentHeaderWithSlot from './preview-examples/content-header-with-slot.vue';
import Content from './preview-examples/content.vue';
import CustomFieldValidation from './preview-examples/custom-field-validation.vue';
@@ -323,6 +324,7 @@ const routes: IxPreviewRoutes = {
'/preview/datetime-range': DatetimeRange,
'/preview/content-header-no-back': ContentHeaderNoBack,
'/preview/content-header': ContentHeader,
+ '/preview/content-header-truncate': ContentHeaderTruncate,
'/preview/content-header-with-slot': ContentHeaderWithSlot,
'/preview/datepicker-range': DatepickerRange,
'/preview/datepicker': Datepicker,
diff --git a/packages/vue-test-app/src/preview-examples/content-header-truncate.vue b/packages/vue-test-app/src/preview-examples/content-header-truncate.vue
new file mode 100644
index 00000000000..341ab36daa8
--- /dev/null
+++ b/packages/vue-test-app/src/preview-examples/content-header-truncate.vue
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+ Button1
+ Button2
+ Button3
+
+
diff --git a/packages/vue-test-app/src/preview-examples/content-header.vue b/packages/vue-test-app/src/preview-examples/content-header.vue
index cc97abfafe7..e70683a98d2 100644
--- a/packages/vue-test-app/src/preview-examples/content-header.vue
+++ b/packages/vue-test-app/src/preview-examples/content-header.vue
@@ -14,8 +14,8 @@ import { IxButton, IxContentHeader } from '@siemens/ix-vue';
Button1
Button2
diff --git a/packages/vue/src/components/ix-content-header.ts b/packages/vue/src/components/ix-content-header.ts
index 93c00ca39d9..1c31715b770 100644
--- a/packages/vue/src/components/ix-content-header.ts
+++ b/packages/vue/src/components/ix-content-header.ts
@@ -10,6 +10,7 @@ export const IxContentHeader: StencilVueComponent = /*@__PU
'variant',
'headerTitle',
'headerSubtitle',
+ 'textOverflow',
'hasBackButton',
'backButtonClick'
], [
diff --git a/testing/framework-tests/__snapshots__/content-header-aria-snapshot.yaml b/testing/framework-tests/__snapshots__/content-header-aria-snapshot.yaml
index fcabc1223ec..ead481fcdc1 100644
--- a/testing/framework-tests/__snapshots__/content-header-aria-snapshot.yaml
+++ b/testing/framework-tests/__snapshots__/content-header-aria-snapshot.yaml
@@ -1,5 +1,5 @@
- button "Arrow Left"
-- text: Content title Subtitle
+- text: Content title that remains compact when horizontal space is limited Supporting context is available from the native tooltip
- button "Button1"
- button "Button2"
- button "Button3"
\ No newline at end of file
diff --git a/testing/framework-tests/__snapshots__/content-header-truncate-aria-snapshot.yaml b/testing/framework-tests/__snapshots__/content-header-truncate-aria-snapshot.yaml
new file mode 100644
index 00000000000..20a0259b1c5
--- /dev/null
+++ b/testing/framework-tests/__snapshots__/content-header-truncate-aria-snapshot.yaml
@@ -0,0 +1,6 @@
+- main:
+ - button "Arrow Left"
+ - text: Content title that remains compact when horizontal space is limited Supporting context is available from the native tooltip
+ - button "Button1"
+ - button "Button2"
+ - button "Button3"
\ No newline at end of file
diff --git a/testing/framework-tests/tests/generated/axe.ts b/testing/framework-tests/tests/generated/axe.ts
index 2f6255f1cba..5daf7ac0e9a 100644
--- a/testing/framework-tests/tests/generated/axe.ts
+++ b/testing/framework-tests/tests/generated/axe.ts
@@ -50,6 +50,7 @@ import './chip-axe.spec.ts';
import './content-axe.spec.ts';
import './content-header-axe.spec.ts';
import './content-header-no-back-axe.spec.ts';
+import './content-header-truncate-axe.spec.ts';
import './content-header-with-slot-axe.spec.ts';
import './custom-field-axe.spec.ts';
import './custom-field-validation-axe.spec.ts';
diff --git a/testing/framework-tests/tests/generated/content-header-truncate-axe.spec.ts b/testing/framework-tests/tests/generated/content-header-truncate-axe.spec.ts
new file mode 100644
index 00000000000..4818b90446a
--- /dev/null
+++ b/testing/framework-tests/tests/generated/content-header-truncate-axe.spec.ts
@@ -0,0 +1,26 @@
+/*
+ * SPDX-FileCopyrightText: 2025 Siemens AG
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/**
+ * This test was autogenerated by 'testing/framework-tests/src/main.ts'
+ * Do not edit this file directly.
+ */
+import { test, expect } from '@playwright/test';
+import { waitForReadiness, createAxeBuilder } from '../utils';
+
+test('content-header-truncate - accessibility check', async ({ page }) => {
+ await page.goto('/preview/content-header-truncate');
+
+ // Ugly and not the reliable way to wait for Stencil to be ready
+ await waitForReadiness(page);
+
+ const accessibilityScanResults = await createAxeBuilder(page).disableRules(['page-has-heading-one']).analyze();
+
+ expect(accessibilityScanResults.violations).toEqual([]);
+});
diff --git a/testing/framework-tests/tests/generated/content-header-truncate.spec.ts b/testing/framework-tests/tests/generated/content-header-truncate.spec.ts
new file mode 100644
index 00000000000..7522bb7151e
--- /dev/null
+++ b/testing/framework-tests/tests/generated/content-header-truncate.spec.ts
@@ -0,0 +1,26 @@
+/*
+ * SPDX-FileCopyrightText: 2025 Siemens AG
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/**
+ * This test was autogenerated by 'testing/framework-tests/src/main.ts'
+ * Do not edit this file directly.
+ */
+import { test, expect } from '@playwright/test';
+import { waitForReadiness } from '../utils';
+
+test('content-header-truncate', async ({ page }) => {
+ await page.goto('/preview/content-header-truncate');
+
+ // Ugly and not the reliable way to wait for Stencil to be ready
+ await waitForReadiness(page);
+
+ await expect(page.locator('body')).toMatchAriaSnapshot({
+ name: 'content-header-truncate.aria-snapshot.yaml',
+ });
+});
diff --git a/testing/framework-tests/tests/generated/index.ts b/testing/framework-tests/tests/generated/index.ts
index a3d295c9fc0..c4ab69e82c6 100644
--- a/testing/framework-tests/tests/generated/index.ts
+++ b/testing/framework-tests/tests/generated/index.ts
@@ -100,6 +100,8 @@ import './content-axe.spec.ts';
import './content-header-axe.spec.ts';
import './content-header-no-back-axe.spec.ts';
import './content-header-no-back.spec.ts';
+import './content-header-truncate-axe.spec.ts';
+import './content-header-truncate.spec.ts';
import './content-header-with-slot-axe.spec.ts';
import './content-header-with-slot.spec.ts';
import './content-header.spec.ts';
diff --git a/testing/framework-tests/tests/generated/test-ids.autogenerated.ts b/testing/framework-tests/tests/generated/test-ids.autogenerated.ts
index b90cd1f6abd..5605db637fd 100644
--- a/testing/framework-tests/tests/generated/test-ids.autogenerated.ts
+++ b/testing/framework-tests/tests/generated/test-ids.autogenerated.ts
@@ -206,6 +206,7 @@ export const testIds = [
"content",
"content-header",
"content-header-with-slot",
+ "content-header-truncate",
"content-header-no-back",
"chip",
"checkbox",
diff --git a/testing/framework-tests/tests/working-with-axe.spec.ts b/testing/framework-tests/tests/working-with-axe.spec.ts
index edd92d36306..447d48b030e 100644
--- a/testing/framework-tests/tests/working-with-axe.spec.ts
+++ b/testing/framework-tests/tests/working-with-axe.spec.ts
@@ -68,6 +68,10 @@ import './generated/popover-axe.spec';
// Chip tests
import './generated/chip-axe.spec';
+// Content header tests
+import './generated/content-header-axe.spec';
+import './generated/content-header-truncate-axe.spec';
+
// Chat tests
import './generated/chat-axe.spec';
import './generated/chat-ai-message-axe.spec';
diff --git a/testing/framework-tests/tests/working.spec.ts b/testing/framework-tests/tests/working.spec.ts
index 14876835fcb..17b2641d4ee 100644
--- a/testing/framework-tests/tests/working.spec.ts
+++ b/testing/framework-tests/tests/working.spec.ts
@@ -68,6 +68,10 @@ import './generated/popover.spec';
// Chip tests
import './generated/chip.spec';
+// Content header tests
+import './generated/content-header.spec';
+import './generated/content-header-truncate.spec';
+
// Chat tests
import './generated/chat.spec';
import './generated/chat-ai-message.spec';
diff --git a/testing/visual-testing/__screenshots__/tests/content-header/content-header.e2e.ts/content-header-basic-1-chromium---classic-dark-linux.png b/testing/visual-testing/__screenshots__/tests/content-header/content-header.e2e.ts/content-header-basic-1-chromium---classic-dark-linux.png
index 1b318c78220..f921289005c 100644
Binary files a/testing/visual-testing/__screenshots__/tests/content-header/content-header.e2e.ts/content-header-basic-1-chromium---classic-dark-linux.png and b/testing/visual-testing/__screenshots__/tests/content-header/content-header.e2e.ts/content-header-basic-1-chromium---classic-dark-linux.png differ
diff --git a/testing/visual-testing/__screenshots__/tests/content-header/content-header.e2e.ts/content-header-basic-1-chromium---classic-light-linux.png b/testing/visual-testing/__screenshots__/tests/content-header/content-header.e2e.ts/content-header-basic-1-chromium---classic-light-linux.png
index 688d1120e47..aa65bab52e9 100644
Binary files a/testing/visual-testing/__screenshots__/tests/content-header/content-header.e2e.ts/content-header-basic-1-chromium---classic-light-linux.png and b/testing/visual-testing/__screenshots__/tests/content-header/content-header.e2e.ts/content-header-basic-1-chromium---classic-light-linux.png differ
diff --git a/testing/visual-testing/__screenshots__/tests/content-header/content-header.e2e.ts/content-header-secondary-1-chromium---classic-dark-linux.png b/testing/visual-testing/__screenshots__/tests/content-header/content-header.e2e.ts/content-header-secondary-1-chromium---classic-dark-linux.png
index 3dfb5a3429b..953bd5f5901 100644
Binary files a/testing/visual-testing/__screenshots__/tests/content-header/content-header.e2e.ts/content-header-secondary-1-chromium---classic-dark-linux.png and b/testing/visual-testing/__screenshots__/tests/content-header/content-header.e2e.ts/content-header-secondary-1-chromium---classic-dark-linux.png differ
diff --git a/testing/visual-testing/__screenshots__/tests/content-header/content-header.e2e.ts/content-header-secondary-1-chromium---classic-light-linux.png b/testing/visual-testing/__screenshots__/tests/content-header/content-header.e2e.ts/content-header-secondary-1-chromium---classic-light-linux.png
index fedff546231..8b9607fc186 100644
Binary files a/testing/visual-testing/__screenshots__/tests/content-header/content-header.e2e.ts/content-header-secondary-1-chromium---classic-light-linux.png and b/testing/visual-testing/__screenshots__/tests/content-header/content-header.e2e.ts/content-header-secondary-1-chromium---classic-light-linux.png differ
diff --git a/testing/visual-testing/tests/content-header/basic/index.html b/testing/visual-testing/tests/content-header/basic/index.html
index 1d877dc1404..0295b46fe9e 100644
--- a/testing/visual-testing/tests/content-header/basic/index.html
+++ b/testing/visual-testing/tests/content-header/basic/index.html
@@ -14,25 +14,17 @@
/>
Stencil Component Starter