Timeline Date-Range Filter · Spec #385

Horizontal bar-chart widget placed between the SearchFilterBar and DocumentList on /documents. Bar height per month reflects document density across the archive. Drag or click to select a date range; the document list filters to that period. Hidden when calendar view is active.

1 · Anatomy

Seven named zones. Every implementation decision maps back to one of these.

① Widget-Header (Label + Gesamtanzahl) ② Gesamtanzahl-Badge
③ Bar (1 Monat)
④ Auswahl-Bereich (navy)
1900 1910 1920 1930 1940 1950
⑤ Linker Zieh-Griff (Start) ⑥ Rechter Zieh-Griff (Ende) ⑦ Löschen-Button (×)
① Widget-Header — Label "Zeitachse" + Gesamtanzahl aller Dokumente im Archiv.
③ Bar — Ein Balken pro Monat. Höhe proportional zur Dokumentenanzahl dieses Monats. Max-Höhe = Monat mit den meisten Dokumenten.
④–⑥ Auswahl — Ausgewählte Balken werden navy (#012851). Zieh-Griffe definieren Start/Ende. ⑦ Löschen-Button erscheint sobald eine Auswahl aktiv ist.

2 · Design-Tokens

Das Widget verwendet ausschließlich diese Tokens — keine Hard-coded-Werte in der Komponente.

Light theme
bg-surface#FFFFFF — Widget-Hintergrund
border-line#D8D6CF — Widget-Rahmen
bar-idle#D4EDE9 — Balken ohne Auswahl (helles Mint)
bar-hover#A1DCD8 — Balken on hover (brand-mint)
bar-selected#012851 — Balken im Auswahl-Bereich (navy)
bar-outside#E8E6E0 — Balken außerhalb der Auswahl (gedimmt)
handle#012851 mit weißem Kern — Zieh-Griff
tooltip-bg#1A1A1A — Tooltip-Hintergrund
Dark theme
bg-surface#0A1218 — Widget-Hintergrund
bar-idle#0E2535 — Balken ohne Auswahl (dunkles Teal)
bar-hover#1E4060 — hover
bar-selected#A1DCD8 — Auswahl in Dark mode (Mint, invertiert)
bar-outside#0A1218 — außerhalb, kaum sichtbar
handle#A1DCD8 mit dunklem Kern

3 · Zustandsmodell

Vier Zustände. Gleiche Markup-Struktur — nur CSS-Klassen und ARIA-Attribute ändern sich.

State AIdle — keine Auswahl
Dokumente durchsuchen …
Sortierung
Filter
Zeitachse 412 Dokumente · 1900–1950
190019101920193019401950
1915 · 24 Dokumente
Brief über die Lage an der Westfront
Karl Raddatz → Elfriede Raddatz
März 1915
Feldpost aus Verdun
Hans Raddatz → Karl Raddatz
Juli 1915
Idle: Alle Balken in hellem Mint. Kein Griff, kein Löschen-Button. Cursor: crosshair über den Balken.
State BHover — Tooltip
Zeitachse 412 Dokumente · 1900–1950
August 1915 · 24 Dok.
190019101920193019401950
Hover: Balken wechselt zu brand-mint. Tooltip erscheint: „Monat Jahr · N Dok." Cursor: pointer.
State CAktive Auswahl — 1914–1920 (Erster Weltkrieg)
Dokumente durchsuchen …
Sortierung
▼ Filter
Jan 1914 – Dez 1920 ×
Zeitachse 187 Dokumente · Jan 1914 – Dez 1920
190019101920193019401950
1915 · 24 Dokumente
Brief über die Lage an der Westfront
Karl Raddatz → Elfriede Raddatz
März 1915
Feldpost aus Verdun
Hans Raddatz → Karl Raddatz
Juli 1915
1914 · 18 Dokumente
Kriegsausbruch — Brief an die Familie
Karl Raddatz → Elfriede Raddatz, Hans Raddatz
Aug 1914
Aktive Auswahl: Balken im Bereich navy, außerhalb gedimmt. Handles links/rechts ziehbar. Label in SearchFilterBar zeigt „Jan 1914 – Dez 1920". Dokumentenliste zeigt nur Dokumente im gewählten Zeitraum.

4 · Dark Mode

Gleiches Verhalten, Farben remappt. Auswahl-Balken wechseln zu Mint (invertiert zu Light mode).

Dark · Idle
Dokumente durchsuchen …
▼ Filter
Zeitachse 412 Dokumente · 1900–1950
190019101920193019401950
Dark · Auswahl aktiv1914–1920
Dokumente durchsuchen …
Jan 1914 – Dez 1920 ×
Zeitachse 187 Dokumente · Jan 1914 – Dez 1920
190019101920193019401950
Dark mode: Ausgewählte Balken = Mint (#A1DCD8), außerhalb = fast schwarz (#0A1218). Badge in SearchFilterBar: Mint-Hintergrund mit Navy-Text.

5 · Verhaltensregeln

SzenarioVerhalten
Click auf einzelnen BalkenSetzt Start und Ende auf denselben Monat → Einzelmonat-Filter. Direkt gefiltert, kein Drag nötig.
Click + DragStart = Mousedown-Position, Ende = Mousemove-Position. Richtung egal (rechts → links und links → rechts erlaubt). Filter wird live aktualisiert während des Drags.
Handle dragStart- oder End-Handle verschieben justiert die bestehende Auswahl. Der andere Handle bleibt fixiert.
Click außerhalb der BalkenAuswahl wird gelöscht. Liste zeigt alle Dokumente.
Löschen-Button (×)Auswahl löschen. Badge in SearchFilterBar verschwindet. Bars kehren zu Idle zurück.
Kombination mit anderen FilternAND-Semantik. Zeitachsen-Filter + Personen-Filter + Tag-Filter sind kumulativ.
Wechsel zu KalenderansichtTimeline-Widget wird ausgeblendet (display:none). Aktive Auswahl bleibt im State erhalten und wird wieder angezeigt beim Rückwechsel.
Monate ohne DokumenteBar hat Mindesthöhe von 2px (sichtbar, aber minimal). Tooltip zeigt „Monat Jahr · 0 Dokumente". Klick auf Null-Bar setzt Single-Month-Filter (leere Liste erwartet).
Granularität (OQ-2)Entscheidung: ganze Monate. Start = 1. des Startmonats, Ende = letzter Tag des Endmonats. Keine Wochen oder Tage auswählbar.
Startmonat beim Öffnen (OQ-1)Entscheidung: auto-derived range. X-Achse leitet sich aus frühestem und spätestem documentDate ab. Kein festes Datum (1880–heute).
Keyboard-ZugangTab fokussiert das Widget. Pfeiltasten verschieben einen virtuellen Cursor Monat für Monat. Enter/Space setzt Start; zweites Enter setzt Ende. Escape bricht Drag ab.
Mobile (<768 px)Widget kollabiert auf kompakte Zeile: „Zeitraum: [Jan 1914 – Dez 1920] ×" — kein Balkendiagramm. Tap öffnet ein Modal mit dem vollen Widget.

6 · Offene Fragen — Entscheidungen

Backend-Anforderung. Neuer Query-Parameter ?dateFrom=YYYY-MM&dateTo=YYYY-MM auf dem bestehenden Search-Endpoint, oder dedizierter Density-Endpoint GET /api/documents/density?from=1900-01&to=1950-12 der [{month:"1915-08", count:24}, ...] zurückgibt. Das bestehende Offset-Paging entfällt für diesen Widget-Call.

7 · Implementation Reference

ElementTailwind-KlassenPixelwertAnmerkung
Widget-Containerbg-surface border border-line rounded-sm shadow-sm px-4 py-3padding 16px 12pxGleiche Karte wie SearchFilterBar und DocumentList
Widget-Header Zeileflex items-center justify-between mb-2margin-bottom 8pxLabel links, Gesamtanzahl rechts
Header Labeltext-[10px] font-bold uppercase tracking-widest text-ink-310px / 700Identisch zu anderen Section-Labels
Gesamtanzahl-Texttext-[10px] text-ink-3 — wenn Auswahl aktiv: text-primary font-bold10pxZeigt gefilterte Anzahl sobald Auswahl aktiv
Bar-Containerflex items-end gap-px h-10 w-fullheight 40pxh-12 (48px) wenn Platz es erlaubt
Bar idleflex-1 rounded-t-sm min-h-[2px] + inline height als %-WertvarCSS custom property --bar-h setzen via JS
Bar: Farbe idlebg-[#D4EDE9] (light) / bg-[#0E2535] (dark)Helles Mint, nicht brand-mint — zu dominant
Bar: Farbe hoverhover:bg-minttransition-colors duration-100
Bar: Farbe selectedbg-primary (light) / bg-mint (dark)Invertierung in Dark mode beabsichtigt
Bar: Farbe outsidebg-mutedGedimmt, aber noch sichtbar
Baselineborder-t border-line mt-0.5 mb-11pxTrennt Balken von X-Achsen-Labels
X-Achse Labelsflex justify-between text-[9px] text-ink-39pxNur Jahres-Labels alle 10 Jahre; mehr wenn Platz
Drag Handleabsolute w-3 h-3 rounded-full bg-primary border-2 border-surface shadow cursor-ew-resize -top-1.5 left-1/2 -translate-x-1/2 z-1012px / 12pxTouch target: mind. 44px durch p-4 auf einem unsichtbaren Wrapper
Tooltipabsolute bottom-full left-1/2 -translate-x-1/2 mb-1 bg-ink text-surface text-[9px] font-bold px-1.5 py-0.5 rounded-sm whitespace-nowrap z-20 pointer-events-none9pxErscheint via group-hover:block hidden
Badge in SearchFilterBarflex items-center gap-1 bg-primary text-primary-fg text-[9px] font-bold px-2 py-1 rounded-sm9pxLöschen-Button innerhalb: ml-1 text-sm leading-none hover:opacity-70
Mobile CollapseWidget: sm:block hidden — Mobile-Badge: sm:hidden flex items-center gap-2Unter 640px nur Badge-Zeile sichtbar, kein Chart
Komponenten-NameTimelineDensityFilter.svelteProps: density: MonthBucket[], bind:from, bind:to, onchange