diff --git a/frontend/src/lib/shared/primitives/MetaLine.svelte b/frontend/src/lib/shared/primitives/MetaLine.svelte
new file mode 100644
index 00000000..77493fd4
--- /dev/null
+++ b/frontend/src/lib/shared/primitives/MetaLine.svelte
@@ -0,0 +1,26 @@
+
+
+{#if items.length > 0}
+
+ {#if iconSrc}
+

+ {/if}
+ {#each items as item, i (i)}
+ {#if i > 0}
+
·
+ {/if}
+
{item}
+ {/each}
+
+{/if}
diff --git a/frontend/src/lib/shared/primitives/MetaLine.svelte.spec.ts b/frontend/src/lib/shared/primitives/MetaLine.svelte.spec.ts
new file mode 100644
index 00000000..3fe4ba4a
--- /dev/null
+++ b/frontend/src/lib/shared/primitives/MetaLine.svelte.spec.ts
@@ -0,0 +1,74 @@
+import { afterEach, describe, expect, it } from 'vitest';
+import { cleanup, render } from 'vitest-browser-svelte';
+import MetaLine from './MetaLine.svelte';
+
+afterEach(() => cleanup());
+
+describe('MetaLine', () => {
+ it('renders N item spans when given N items', async () => {
+ render(MetaLine, { items: ['14. März 1923', '14 Dokumente', '4 Personen'] });
+ const spans = document.querySelectorAll('[data-testid="meta-item"]');
+ expect(spans).toHaveLength(3);
+ expect(spans[0].textContent).toBe('14. März 1923');
+ expect(spans[1].textContent).toBe('14 Dokumente');
+ expect(spans[2].textContent).toBe('4 Personen');
+ });
+
+ it('renders separator spans between items', async () => {
+ render(MetaLine, { items: ['A', 'B', 'C'] });
+ const seps = document.querySelectorAll('[data-testid="meta-sep"]');
+ // N items → N-1 separators
+ expect(seps).toHaveLength(2);
+ expect(seps[0].textContent).toBe('·');
+ });
+
+ it('renders nothing when items is empty', async () => {
+ const { container } = render(MetaLine, { items: [] });
+ // No element children — Svelte may leave an empty comment node but no DOM elements
+ expect(container.querySelectorAll('[data-testid]')).toHaveLength(0);
+ expect(container.querySelectorAll('div, span, img')).toHaveLength(0);
+ });
+
+ it('renders nothing when items has one element (no separator)', async () => {
+ render(MetaLine, { items: ['Nur eines'] });
+ const seps = document.querySelectorAll('[data-testid="meta-sep"]');
+ expect(seps).toHaveLength(0);
+ const spans = document.querySelectorAll('[data-testid="meta-item"]');
+ expect(spans).toHaveLength(1);
+ });
+
+ it('shows the leading img when iconSrc is supplied', async () => {
+ render(MetaLine, {
+ items: ['Datum'],
+ iconSrc: '/degruyter-icons/Simple/Small-16px/SVG/Action/Calendar-Add-SM.svg'
+ });
+ const img = document.querySelector('img');
+ expect(img).not.toBeNull();
+ });
+
+ it('does NOT render an img when iconSrc is omitted', async () => {
+ render(MetaLine, { items: ['Datum'] });
+ const img = document.querySelector('img');
+ expect(img).toBeNull();
+ });
+
+ it('icon has width 14px, height 14px, opacity 0.5, and alt=""', async () => {
+ render(MetaLine, {
+ items: ['Datum'],
+ iconSrc: '/degruyter-icons/Simple/Small-16px/SVG/Action/Calendar-Add-SM.svg'
+ });
+ const img = document.querySelector('img') as HTMLImageElement;
+ expect(img.alt).toBe('');
+ // Inline style values (set directly on the element, not via getComputedStyle)
+ expect(img.style.width).toBe('14px');
+ expect(img.style.height).toBe('14px');
+ expect(img.style.opacity).toBe('0.5');
+ });
+
+ it('applies font-size 12px to the wrapper', async () => {
+ render(MetaLine, { items: ['Test'] });
+ const wrapper = document.querySelector('[data-testid="meta-line"]') as HTMLElement;
+ expect(wrapper).not.toBeNull();
+ expect(wrapper.style.fontSize).toBe('12px');
+ });
+});