diff --git a/frontend/src/lib/timeline/GapSpan.svelte b/frontend/src/lib/timeline/GapSpan.svelte
new file mode 100644
index 00000000..ba8aa18a
--- /dev/null
+++ b/frontend/src/lib/timeline/GapSpan.svelte
@@ -0,0 +1,20 @@
+
+
+
+
+ {yearLabel} · {m.timeline_gap_empty()}
+
+
diff --git a/frontend/src/lib/timeline/GapSpan.svelte.spec.ts b/frontend/src/lib/timeline/GapSpan.svelte.spec.ts
new file mode 100644
index 00000000..57df8a33
--- /dev/null
+++ b/frontend/src/lib/timeline/GapSpan.svelte.spec.ts
@@ -0,0 +1,20 @@
+import { describe, it, expect, afterEach } from 'vitest';
+import { cleanup, render } from 'vitest-browser-svelte';
+import GapSpan from './GapSpan.svelte';
+
+afterEach(() => cleanup());
+
+describe('GapSpan', () => {
+ it('renders a multi-year empty run as "{from}–{to} · keine Einträge" (REQ-015)', () => {
+ render(GapSpan, { from: 1910, to: 1914 });
+ expect(document.body.textContent).toContain('1910–1914');
+ expect(document.body.textContent).toContain('keine Einträge');
+ });
+
+ it('renders a single empty year as "{year} · keine Einträge" (REQ-015)', () => {
+ render(GapSpan, { from: 1912, to: 1912 });
+ expect(document.body.textContent).toContain('1912');
+ expect(document.body.textContent).not.toContain('1912–1912');
+ expect(document.body.textContent).toContain('keine Einträge');
+ });
+});