# Lesereisen — Reader-Integration

Typauswahl bei `/geschichten/new`, Journey-Badge auf der Übersichtsliste und die neue geordnete Leseansicht auf `/geschichten/[id]` wenn `type === 'JOURNEY'`. Bestehende Story-Ansichten bleiben unverändert.

Familienarchiv
Final Spec
2026-06-07 · @leonievoss
Issue #752
R

## Lesereisen — Reader

Alle angemeldeten Familienmitglieder können Lesereisen entdecken und in Briefsequenzen mit Kuratoren-Notizen eintauchen. BLOG_WRITERs sehen zusätzlich Bearbeiten/Löschen-Aktionen.

/geschichten · /geschichten/new · /geschichten/[id]
Konzept

Eine Lesereise ist eine `Geschichte` mit `type === 'JOURNEY'`. Ihr Kerninhalt ist eine geordnete Sequenz von Briefen (`JourneyItem`s mit `document_id`) und Zwischentexten (`JourneyItem`s ohne `document_id`). Das optionale Feld `body` dient als Einleitung/Preface.

Diese Spec deckt drei Änderungen ab: (1) die Typauswahl auf `/geschichten/new` als vorgelagerter Schritt, (2) das „REISE"-Badge in der Übersichtsliste, und (3) die neue Journey-Leseansicht auf der Detailseite, die den bestehenden Prosa-Body durch eine nummerierte Briefliste ersetzt.

Dokument-Items zeigen Titel, Datum, Sender→Empfänger und einen Link zum Brief. Optionale Kuratoren-Notizen erscheinen als Annotation mit Mint-Linker-Rand unter dem Briefeintrag. Interlude-Items (kein Dokument) erscheinen als eingerückte Absätze mit orangenem linken Rand — klar vom Dokumenttyp unterscheidbar, aber harmonisch im Lesefluss.

Screens — Typauswahl

### LR-0 — Typauswahl /geschichten/new

Issue #752 · LR-0

Neuer vorgelagerter Schritt beim Erstellen einer Geschichte. Zwei Karten zur Auswahl: „Geschichte" (Prosa) und „Lesereise" (Briefsequenz). Die ausgewählte Karte wird hervorgehoben. Erst nach Auswahl wird der „Weiter"-Button aktiv. Auswahl bleibt im URL-Param erhalten (`?type=JOURNEY`).

Varianten: Keine Auswahl (Weiter-Button inaktiv) · Lesereise gewählt (hier gezeigt) · Geschichte gewählt

Desktop — 1040px · Lesereise gewählt
←
Neue Geschichte
ENTWURF
Was möchtest du erstellen?
✍️
Geschichte
Freier Prosatext über Familienerlebnisse, Erinnerungen oder historische Einordnungen — mit verlinkten Personen und Dokumenten.
📜
Lesereise
Geordnete Briefsequenz mit optionalen Kuratoren-Notizen zwischen den Briefen — für chronologische Korrespondenz-Sammlungen.

#### impl-ref — LR-0 Typauswahl

Element | Wert | Hinweise
--- | --- | ---
Layout
Selector area | flex flex-1 items-center justify-center bg-canvas px-6 py-10 | zentriert, füllt restliche Höhe
Frage | font-serif text-sm text-ink-2 text-center mb-4 |
Karten-Grid | flex gap-4 | 2 gleich breite Karten; auf Mobile flex-col
Type-Karte
Karte (inaktiv) | border border-line rounded-md p-4 bg-white cursor-pointer hover:border-primary hover:bg-surface | focus-visible:ring-2 focus-visible:ring-primary
Karte (ausgewählt) | border-2 border-orange-500 bg-orange-50 shadow-sm | aria-pressed="true"; kein Tailwind-Kürzel — nutze CSS-var(--orange)
Check-Kreis | w-5 h-5 rounded-full bg-orange-500 flex items-center justify-center self-end mt-2 | nur sichtbar wenn ausgewählt
Kartentitel | font-serif text-sm text-ink |
Kartenbeschreibung | text-xs text-ink-3 leading-relaxed mt-1 |
Navigation
Weiter-Button | rounded border border-primary bg-primary text-white px-4 py-2 text-sm font-medium disabled:opacity-40 | disabled wenn keine Karte ausgewählt
URL-Param | ?type=STORY | ?type=JOURNEY | per goto() nach Klick auf Weiter; lesefreundlich bookmarkbar
Mobile | flex-col Karten; volle Breite | kein Scrollbedarf auf 320px

Screens — Übersichtsliste

### LR-1 — Reise-Badge in /geschichten

Issue #752 · LR-1

Die Übersichtsliste erhält ein kleines „REISE"-Badge in der Metaspalte einer Journey-Zeile — unterhalb von Datum und Personenchip. Zeilen mit `type === 'STORY'` bleiben unverändert. Das Badge ist nicht klickbar, dient als reine visuelle Unterscheidung.

Varianten: Mischte Liste (hier gezeigt) · Nur-Journey-Filter · Nur-Story-Ansicht (unverändert)

Desktop — 1040px · gemischte Liste
Geschichten
Alle Franz Raddatz Emma Müller + Person wählen
MR
Maria Raddatz
14. März 2025
FR Franz Raddatz
Der Sommer in Breslau
Oma erzählte oft vom letzten Sommer vor dem Krieg, als die Familie noch vollständig zusammen war und niemand ahnte, was kommen würde…
KR
Klaus Raddatz
15. Mai 2025
FR Franz Raddatz REISE
Briefe aus Breslau 1938–1942
Eine Lesereise durch den Briefwechsel zwischen Franz und Emma — von den letzten Friedenssommern bis zum Ende des Krieges.
GK
Gertrud Koch
18. Okt. 2024
EM Emma Müller
Die Hochzeit im Krieg
1943, mitten im Chaos — Emma bestand darauf, dass das Fest stattfand. Ihr Bruder kam auf Fronturlaub, drei Tage nur, aber es reichte…
Mobile — 320px
9:41●●●
Geschichten
Alle Franz Raddatz + Person…
MR
Maria Raddatz 14. Mrz. 2025
Der Sommer in Breslau
Oma erzählte oft vom letzten Sommer vor dem Krieg…
KR
Klaus Raddatz 15. Mai 2025
Briefe aus Breslau 1938–1942
REISE
Eine Lesereise durch den Briefwechsel zwischen Franz und Emma…

#### impl-ref — LR-1 Journey-Badge in der Liste

Element | Wert | Hinweise
--- | --- | ---
Badge
Journey badge | inline-flex items-center px-1.5 py-px rounded-sm text-[10px] font-bold uppercase tracking-wide bg-orange-50 text-orange-700 border border-orange-200 | nur wenn type === 'JOURNEY'
Position Desktop | unterhalb Datum-Text und Personenchip in der Metaspalte (g-meta) | kein extra Abstand nötig — gap-1 der Flex-Spalte reicht
Position Mobile | inline flex items-center gap-1.5 neben Titel | Titel + Badge in einem flex-Wrapper; badge shrink-0
aria-label | aria-label="Lesereise" | Badge ist span, kein interaktives Element
Bedingte Logik
Svelte guard | {#if geschichte.type === 'JOURNEY'}<span …>REISE</span>{/if} | kein Badge für STORY

Screens — Journey-Leseansicht

### LR-2 — Journey-Detail /geschichten/[id]

Issue #752 · LR-2

Wenn `type === 'JOURNEY'` ersetzt die geordnete Briefliste den Prosa-Body. Optional zeigt ein Einleitungsabsatz (`body`) vor den Items. Jedes Item ist entweder ein Briefeintrag (Kartentitel, Datum, Link) oder ein Interlude-Absatz (orangener linker Rand, kursiv). Die Reihenfolge ergibt sich von oben nach unten — keine Nummern. Briefeinträge können eine optionale Kuratoren-Annotation unter dem Link zeigen.

Varianten: Leserin ohne Schreibrecht · BLOG_WRITER (Bearbeiten/Löschen sichtbar — hier gezeigt) · Mobile

Desktop — 1040px · BLOG_WRITER-Ansicht
Zurück zu Geschichten
LESEREISE
Briefe aus Breslau 1938–1942
KR
Klaus Raddatz
zusammengestellt am 15. Mai 2025
Löschen
Der Briefwechsel zwischen Franz Raddatz und seiner Schwester Emma umspannt vier Jahre — von den letzten unbeschwerten Sommerwochen 1938 bis zum Kriegsende. Diese Lesereise folgt den Briefen in chronologischer Reihenfolge.
Brief vom 12. Juli 1938
12. Juli 1938 · von Franz Raddatz an Emma Müller
Brief öffnen
Im Sommer 1938 schrieb Franz voller Zuversicht — er hatte kaum eine Ahnung, wie bald sich die Welt um ihn herum verändern würde. Seine Briefe aus dieser Zeit tragen eine Leichtigkeit, die in den späteren Kriegsjahren vollständig verschwindet.
Postkarte aus Breslau, August 1938
22. Aug. 1938 · von Franz Raddatz an Emma Müller
Brief öffnen
Diese Karte ist ungewöhnlich kurz für Franz — vier Zeilen, fast hastig. Ein Zeichen der aufkommenden Unruhe in den Nachrichten, oder schlicht die Hitze des Augusts?
Brief vom 3. September 1939
3. Sept. 1939 · von Emma Müller an Franz Raddatz
Brief öffnen
Mobile — 320px · Leserin
9:41●●●
Zurück
LESEREISE
Briefe aus Breslau 1938–1942
KR
Klaus Raddatz
15. Mai 2025
···
Der Briefwechsel zwischen Franz und Emma — von 1938 bis Kriegsende.
Brief vom 12. Juli 1938
12. Juli 1938 · Franz → Emma
Brief öffnen →
Im Sommer 1938 schrieb Franz voller Zuversicht — er hatte kaum eine Ahnung, wie bald sich die Welt um ihn herum verändern würde.
Postkarte Aug. 1938
22. Aug. 1938 · Franz → Emma
Brief öffnen →

#### impl-ref — LR-2 Journey-Leseansicht

Element | Wert | Hinweise
--- | --- | ---
Seitenstruktur
Bedingte Logik | {#if geschichte.type === 'JOURNEY'} JourneyReader {:else} StoryReader {/if} | in +page.svelte von /geschichten/[id]
Artikel-Container | max-w-7xl mx-auto px-4 py-8; innere Lesespalte: max-w-3xl mx-auto | gleich wie StoryReader (R-2)
Artikel-Sheet | rounded-sm border border-line bg-sheet shadow-sm px-5 py-6 sm:px-10 sm:py-10 | Lesebogen-Panel zwischen Canvas und weißen Karten (Token --color-sheet), gleich wie Story (R-2); BackButton bleibt außerhalb
Journey-Badge | inline-flex px-2 py-px rounded-sm text-[10px] font-bold uppercase tracking-widest bg-journey-tint text-journey border border-journey-border mb-2 | über dem Titel; nicht für STORY
Titel | font-serif text-3xl text-ink leading-tight mb-4 | gleich wie Story
Metabar | flex items-center gap-3 pb-4 border-b border-subtle mb-4 | gleich wie Story
Bearbeiten/Löschen | nur BLOG_WRITE; auf Mobile im ··· BottomSheet (implementiert: inline in der Metazeile auf allen Breiten) | gleich wie Story
Intro-Absatz
Intro (body) | font-serif text-lg text-ink-2 italic leading-relaxed mb-6 pb-4 border-b border-dashed border-subtle | nur rendern wenn body nicht leer; kein HTML-Rendering — plaintext
Dokument-Item
Item-Zeile | mb-3 | kein flex nötig — Karte ist full-width
Dokumentkarte | bg-surface border border-line rounded-sm p-3 |
Brieftitel | font-serif text-base text-ink leading-snug mb-0.5 | document.title
Briefmeta | text-sm text-ink-3 mb-2 | formatDate(document.documentDate) · "von X an Y"
Brief öffnen Link | inline-flex items-center gap-1 text-sm font-semibold text-ink hover:text-primary | href="/documents/{item.document.id}"
Kuratoren-Annotation
Annotation | mt-3 pl-3 border-l-2 border-brand-mint bg-muted rounded-r-sm py-1.5 pr-2 | nur rendern wenn item.note vorhanden
Annotations-Text | text-base italic text-ink-2 leading-relaxed |
Interlude-Item
Interlude-Block | pl-3 border-l-2 border-journey-border bg-journey-tint rounded-r-sm py-2 pr-3 my-4 | item.document === null
Interlude-Text | text-base italic text-ink leading-relaxed | item.note; plaintext
Mobile
··· Menü | ml-auto text-ink-3; öffnet BottomSheet mit Bearbeiten + Löschen (implementiert: kein BottomSheet — Aktionen inline) | BLOG_WRITE; gleich wie Story
Touch Target (Brief öffnen) | min-h-[44px] durch padding auf der Karte | WCAG 2.2 AA

## Implementation Guide — Lesereisen Reader

### Geänderte Views und Routen

View | Route | Änderung
--- | --- | ---
Neue Geschichte | /geschichten/new | Neuer Typauswahl-Schritt als first render; setzt ?type=STORY|JOURNEY
Geschichten-Liste | /geschichten | Journey-Badge in GeschichtenCard wenn type === 'JOURNEY'
Geschichte-Detail | /geschichten/[id] | Bedingte Verzweigung: JourneyReader | StoryReader

### Neue Komponenten

- `JourneyReader.svelte` — rendert Intro + Items-Liste; Props: `geschichte: GeschichteDetail`
- `JourneyItemCard.svelte` — ein Dokument-Item mit optionaler Annotation; Props: `item: JourneyItem, position: number`
- `JourneyInterlude.svelte` — ein reiner Text-Interlude; Props: `note: string`

### Datenmodell (nach #750)

- `GeschichteType: 'STORY' | 'JOURNEY'`
- `JourneyItem: { id: UUID, position: number, document: DocumentSummary | null, note: string | null }`
- `Geschichte.items` — geordnete Liste (nach `position` ASC); für STORY leer
- `Geschichte.body` — für JOURNEY der optionale Einleitungstext (plaintext, kein HTML); für STORY der Rich-Text-Body

### Typauswahl — Implementierungshinweise

- Die Typauswahl ist ein Schritt INNERHALB der `/geschichten/new`-Route — kein eigener URL, kein `goto()`. Zustand `let selectedType: GeschichteType | null = null` in der Komponente.
- Erst wenn `selectedType !== null` ist der „Weiter"-Button aktiviert (`disabled={!selectedType}`).
- Nach Klick auf „Weiter": wenn `selectedType === 'JOURNEY'` → `goto('/geschichten/new?type=JOURNEY')` und zeige den Journey-Editor (aus Issue #753); wenn `STORY` → bestehender GeschichteEditor (unverändert).
- Die Karten verwenden `role="radio"` und `aria-checked` für Accessibility. Keyboard: Arrow-Keys wechseln zwischen den Karten, Space/Enter wählt aus.

### Journey-Badge — Implementierungshinweise

- Badge nur in `GeschichtenCard.svelte` hinzufügen — keine Änderung an der Listenlogik oder dem API-Aufruf.
- Text: „REISE" (Kurzform für die Metaspalte); `aria-label="Lesereise"` für den Badge-Span.

### Journey-Reader — Implementierungshinweise

- Items werden bereits geordnet vom Backend geliefert (`ORDER BY position ASC`). Keine client-seitige Sortierung nötig.
- Ein Item ist Interlude wenn `item.document === null`. In diesem Fall: `JourneyInterlude`-Komponente rendern.
- Der Intro-Absatz (`body`) wird als Plaintext gerendert — nicht als innerHTML. Im Editor wird es als einfaches Textarea gespeichert, kein HTML.

### Berechtigungen

- „Bearbeiten" und „Löschen" nur für `currentUser.permissions.includes('BLOG_WRITE')` — gleich wie Story.
- Auf Mobile: Bearbeiten/Löschen im BottomSheet hinter ··· — gleich wie Story. (implementiert: Aktionen bleiben inline in der Metazeile — h-11 Touch-Targets)

### Barrierefreiheit

- Items-Liste: `<ol>` semantisch für die geordnete Briefliste. Interludes sind `<li>`-Elemente mit `aria-label="Kuratorennotiz"`.
- „Brief öffnen"-Link: beschreibender Text mit Briefdatum im `aria-label`, z.B. `aria-label="Brief vom 12. Juli 1938 öffnen"`.
- Touch-Targets: jede Dokumentkarte hat mindestens 44px Höhe durch den Padding der Karte.
- Fokusring: `focus-visible:ring-2 focus-visible:ring-primary` auf allen Links.