# Transcription Read Mode — Final Spec

A focused reading experience for completed transcriptions. Uses the clean split layout: PDF scan on the left, flowing prose on the right. All editing chrome is stripped — no block borders, no comment threads, no toolbars. The text reads like a letter, not like an editing interface.

Familienarchiv
Final
2026-04-05 · @leonievoss
Design rationale

Transcribe mode is for editing. But most visits to a completed transcription are for reading — comparing the handwriting with the typed text, sharing with family, or just revisiting a letter. Read mode strips away all editing chrome and presents the transcription as flowing prose alongside the original scan.

The clean split was chosen over the full-page reader (PDF hidden) and the interleaved view (cropped PDF per block) because it preserves the familiar side-by-side layout from transcribe mode while dramatically reducing visual noise. Users can switch between reading and editing without re-learning the spatial layout.

Kept

- Transcription text (flowing prose)
- PDF scan viewer (same position)
- Topbar (title, Details toggle, person chips)
- Mode switcher (Lesen / Bearbeiten)
- Resizable split handle
- Scroll sync (click paragraph ↔ PDF)

Removed

- Block borders & numbered badges
- Contenteditable / cursor
- Comment threads & “Kommentieren” buttons
- Presence dots & user indicators
- Hint strip (“Markiere eine Passage…”)
- Drag handles, sort controls
- Auto-save status indicator
- Add-block CTA
- History / “Verlauf” button

### S1 — Desktop read mode

S1
Side-by-side split: PDF scan on the left with dimmed annotation outlines, flowing serif prose on the right. The mode switcher shows “Lesen” as active. Clicking a paragraph briefly highlights the matching PDF region (turquoise flash, 1.5s fade).
Primary reading state — the default view when a transcription exists.
Desktop · 1040px
←
Brief von Heinrich an Martha, 14. Mai 1943
HR
Heinrich R.
→
MR
Martha R.
Details ▼
LesenBearbeiten
Liebe Martha,
Dein Heinrich
Liebe Martha,
ich schreibe Dir heute aus dem Lazarett in Breslau. Mach Dir keine Sorgen, es geht mir den Umständen entsprechend gut. Der Arzt sagt [unleserlich] Wochen noch dauern wird.
Die Kinder sollen wissen, dass ich an sie denke. Sag dem kleinen Fritz, er soll auf seine Mutter aufpassen. Und Lotte soll weiter so fleißig in der Schule sein.
In ewiger Liebe,
Dein Heinrich
4 Abschnitte Zuletzt bearbeitet: Oma Inge, 14:23

#### S1 · Desktop read mode

    /* Same side-by-side layout as transcribe mode, but the right panel renders
     * the transcription as continuous flowing prose instead of block cards.
     *
     * Key differences from transcribe mode:
     * - No block borders, headers, footers, or numbered badges
     * - No contenteditable — text is plain rendered HTML
     * - No comment threads, no "Kommentieren" buttons
     * - No presence dots, no hint strip, no auto-save indicator
     * - Annotation rects on PDF are dimmed (opacity ~0.3, no badges)
     * - Still clickable for scroll-sync
     * - Status bar shows: "4 Abschnitte · Zuletzt bearbeitet: Oma Inge, 14:23"
     *
     * Scroll sync:
     * - Click paragraph → matching PDF annotation flashes turquoise (1.5s fade)
     * - Click PDF annotation → matching paragraph gets subtle bg highlight (1.5s fade)
     * - PDF auto-scrolls to center the annotation in the viewport */

Element | Value | Notes
--- | --- | ---
Text panel
Font | Tinos (serif), 16px, line-height 1.85 | Generous reading typography
Padding | 24px 32px | Comfortable margins like a book page
Paragraphs | One <p> per transcription block | mb-4 between paragraphs
[unleserlich] | italic, text-ink-2, font-size: 0.9em | Subtle but readable
Hover | Subtle turquoise bg at 6% opacity | Hint that paragraphs are clickable
PDF panel
Annotations | Dimmed: border-opacity 0.3, bg-opacity 0.04 | Still clickable for scroll-sync
Badges | Hidden | No numbered circles in read mode
Scroll sync | Click para → PDF scrolls, flash 1.5s | Turquoise tint at 18% → fade to 4%
Status bar
Content | "N Abschnitte · Zuletzt bearbeitet: Name, HH:mm" | Uses most recent updated_at across blocks
Height | 28px, sand background | Same as transcribe mode status bar

### S2 — Scroll sync highlight

S2
The user clicked the second paragraph. The matching PDF annotation flashes with a turquoise highlight that fades over 1.5 seconds. The paragraph itself gets a subtle background tint. This is the only interactive element in read mode — no editing, no comments.
Click-to-highlight interaction — bidirectional scroll sync between text and scan.
Desktop · 1040px
←
Brief von Heinrich an Martha, 14. Mai 1943
HR
Heinrich R.
→
MR
Martha R.
Details ▼
LesenBearbeiten
Liebe Martha,
Dein Heinrich
Liebe Martha,
ich schreibe Dir heute aus dem Lazarett in Breslau. Mach Dir keine Sorgen, es geht mir den Umständen entsprechend gut. Der Arzt sagt [unleserlich] Wochen noch dauern wird.
Die Kinder sollen wissen, dass ich an sie denke. Sag dem kleinen Fritz, er soll auf seine Mutter aufpassen. Und Lotte soll weiter so fleißig in der Schule sein.
In ewiger Liebe,
Dein Heinrich
4 Abschnitte Zuletzt bearbeitet: Oma Inge, 14:23

#### S2 · Scroll sync highlight

    /* Bidirectional scroll sync with visual feedback.
     *
     * Text → PDF:
     *   1. User clicks a paragraph
     *   2. Paragraph gets .highlighted class (turquoise bg at 10%)
     *   3. Matching annotation rect gets .highlight-flash class
     *   4. PDF viewport scrolls to center the annotation
     *   5. Both highlights fade over 1.5s via CSS animation
     *
     * PDF → Text:
     *   1. User clicks a dimmed annotation rect
     *   2. Annotation flashes (same .highlight-flash)
     *   3. Matching paragraph gets .highlighted
     *   4. Text panel scrolls to center the paragraph
     *   5. Both fade over 1.5s
     *
     * Implementation: each paragraph has data-block-id matching the
     * transcription block's annotation_id. The annotation rects already
     * have annotation IDs from transcribe mode. */

Element | Value | Notes
--- | --- | ---
Highlight animation
Paragraph bg | rgba(0,199,177,.10) | Turquoise at 10%, fades to 0
Annotation flash | rgba(0,199,177,.18) → .04 | Border returns to .3 opacity
Duration | 1.5s ease-out | CSS animation, no JS timers needed
Scroll behavior | smooth, block: center | scrollIntoView({ behavior: 'smooth', block: 'center' })
Data binding
Paragraph attr | data-block-id="{annotation_id}" | Links text to PDF annotation
Annotation attr | data-annotation-id="{id}" | Already exists from transcribe mode

### S3 — No transcription (empty state)

S3
When no transcription blocks exist, the mode switcher defaults to “Bearbeiten” and the right panel shows an empty state encouraging the user to start transcribing. The “Lesen” tab is disabled (greyed out).
Empty state — no read mode available until at least one block exists.
Desktop · 1040px
←
Brief von Heinrich an Martha, 14. Mai 1943
Details ▼
LesenBearbeiten
Liebe Martha,
Dein Heinrich
✎
Noch keine Transkription
Zeichne Bereiche auf dem Scan und tippe den Text ab, um eine Transkription zu erstellen.

#### S3 · Empty state

    /* When transcription_blocks count is 0:
     * - Mode switcher defaults to "Bearbeiten"
     * - "Lesen" tab is disabled: opacity 0.35, cursor: not-allowed, not clickable
     * - Right panel shows empty state with pencil icon, title, and description
     * - No status bar (nothing to show)
     *
     * As soon as the first block is saved, "Lesen" becomes enabled.
     * The mode does NOT auto-switch — user stays in Bearbeiten. */

Element | Value | Notes
--- | --- | ---
Empty state
Icon | Pencil in 48px sand circle | Centered vertically in panel
Title | "Noch keine Transkription" | i18n key: transcription_empty_title
Description | "Zeichne Bereiche auf dem Scan…" | i18n key: transcription_empty_desc
Mode switcher
Lesen tab | Disabled: opacity .35, not-allowed | Enabled when block count > 0
Default | "Bearbeiten" active | Enters transcribe mode directly

### S4 — Mobile read mode

S4
On mobile, the split view becomes vertical: a collapsible PDF strip (70px) at the top, flowing text below. The mode switcher abbreviates “Bearbeiten” to “Bearb.” to fit. Tapping the PDF strip expands it; tapping again collapses. Paragraphs are still tappable for scroll-sync.
Mobile layout — stacked vertical with collapsible scan strip.
Mobile · 320px
14:23••• WiFi 🔋
← Brief von Heinrich, 14.05.1943
LesenBearb.
Liebe Martha,
▲ Scan vergrößern

Liebe Martha,

ich schreibe Dir heute aus dem Lazarett in Breslau. Mach Dir keine Sorgen, es geht mir den Umständen entsprechend gut. Der Arzt sagt [unleserlich] Wochen noch dauern wird.

Die Kinder sollen wissen, dass ich an sie denke. Sag dem kleinen Fritz, er soll auf seine Mutter aufpassen. Und Lotte soll weiter so fleißig in der Schule sein.

In ewiger Liebe,
Dein Heinrich

4 Abschnitte Oma Inge, 14:23
Mobile · 320px · scan expanded
14:23••• WiFi 🔋
← Brief von Heinrich, 14.05.1943
LesenBearb.
Liebe Martha,
Dein Heinrich
▼ Scan verkleinern

Liebe Martha,

ich schreibe Dir heute aus dem Lazarett in Breslau…

#### S4 · Mobile read mode

    /* On viewports < 768px, the side-by-side split becomes vertical:
     * - PDF scan strip at top (collapsed: 70px, expanded: ~50vh)
     * - Flowing text below, full-width
     * - Tap PDF strip to toggle expand/collapse
     * - Expand hint text: "▲ Scan vergrößern" / "▼ Scan verkleinern"
     *
     * Mode switcher abbreviates: "Lesen | Bearb."
     * Scroll-sync: tapping a paragraph briefly highlights the matching
     * region in the expanded PDF. If PDF is collapsed, it auto-expands
     * first, then scrolls to the annotation.
     *
     * Same status bar at the bottom, same flowing prose styling. */

Element | Value | Notes
--- | --- | ---
PDF strip
Collapsed height | 70px | Shows miniature scan preview
Expanded height | ~50vh or max 300px | Smooth CSS transition (300ms ease)
Toggle | Tap anywhere on strip | Hint text in bottom-right corner
Text area
Font | Tinos, 15px, line-height 1.9 | Slightly larger than desktop for touch
Padding | 16px | Full-width, no wasted space
Mode switcher
Labels | "Lesen | Bearb." | Abbreviated to fit mobile topbar
Font size | 10px | Compact but readable
Scroll sync on mobile
Tap paragraph | Expand PDF if collapsed, then highlight | Auto-expand + scroll + flash
Tap annotation | Collapse PDF, scroll text to paragraph | Smart collapse after showing match

### S5 — Mode switcher states

S5
The mode switcher is a segmented control in the topbar that replaces the previous “Transkribieren” turquoise button. It governs three visual states: Lesen (read mode, this spec), Bearbeiten (transcribe/edit mode), and the existing Annotieren button (yellow comment annotations). The modes are mutually exclusive.
Segmented control — replacing the turquoise “Transkribieren” button.
Lesen active
LesenBearbeiten
✎ Annotieren
Bearbeiten active
LesenBearbeiten
✎ Annotieren
Annotieren active (separate button)
LesenBearbeiten
✎ Annotieren
No transcription blocks
LesenBearbeiten
✎ Annotieren

#### S5 · Mode switcher states

    /* Three mutually exclusive modes:
     *
     * 1. Lesen (read)      — this spec. Flowing prose, dimmed annotations, no editing.
     * 2. Bearbeiten (edit)  — annotation-transcription-final-spec. Block cards, contenteditable.
     * 3. Annotieren         — yellow comment annotations on PDF. Separate button, not in segmented control.
     *
     * The segmented control only contains Lesen + Bearbeiten.
     * Annotieren is a separate button that, when active, deselects both Lesen and Bearbeiten
     * (both appear deselected/dimmed in the segmented control).
     *
     * When the user clicks Annotieren while in read/transcribe mode:
     *   → Enter annotate mode, both segmented items dim
     * When the user clicks a segmented item while in annotate mode:
     *   → Exit annotate mode, enter the selected mode
     *
     * State: let mode: 'read' | 'transcribe' | 'annotate' = $state(...)
     * Default: 'read' if blocks.length > 0, else 'transcribe'
     * The "Annotieren" button is hidden if !canAnnotate || !isPdf */

Element | Value | Notes
--- | --- | ---
Segmented control
Items | "Lesen" | "Bearbeiten" | Mobile: "Lesen" | "Bearb."
Active style | bg:navy, color:#fff | Rounded within the pill
Inactive style | bg:transparent, color:muted | Hover: bg:sand
Dimmed style | Both items at opacity .5 | Only when annotate mode is active
Disabled (Lesen) | opacity .35, cursor not-allowed | When no transcription blocks exist
Annotieren button
Default | Ghost style (border:muted) | Same as current topbar button
Active | bg:navy, color:#fff | Filled state when annotate mode on
Visibility | canAnnotate && isPdf | Hidden for non-PDF documents
Accessibility
Segmented | role="tablist", children role="tab" | aria-selected on active tab
Annotieren | aria-pressed={annotateMode} | Toggle button semantics
Disabled tab | aria-disabled="true", tabindex="-1" | Not focusable when no blocks

## Implementation Guide — Transcription Read Mode (Clean Split)

### 1. Overview

Read mode is the default view for documents that have transcription blocks. It reuses the same side-by-side split layout as transcribe mode but replaces the editable block cards with flowing serif prose. The goal is a distraction-free reading experience that still lets users compare handwriting with typed text.

### 2. Mode State Management

The document detail page manages a single `mode` state that governs the entire view:

    let mode: 'read' | 'transcribe' | 'annotate' = $state(
      blocks.length > 0 ? 'read' : 'transcribe'
    );

- `mode === 'read'` → this spec (flowing prose, dimmed annotations, no editing)
- `mode === 'transcribe'` → annotation-transcription-final-spec (block cards, contenteditable)
- `mode === 'annotate'` → yellow comment annotations on PDF
- The segmented control in the topbar toggles between `'read'` and `'transcribe'`.
- The “Annotieren” button toggles `'annotate'` on/off. When entering annotate mode, the previous mode (read or transcribe) is stored so the user returns to it when exiting.

### 3. Component Architecture

#### 3a. New components

Component | Purpose
--- | ---
`TranscriptionReadView.svelte` | Right panel content in read mode. Renders transcription blocks as flowing prose (`<article>` with `<p>` per block). Handles scroll-sync click handlers.
`ModeSwitcher.svelte` | Segmented control (`Lesen | Bearbeiten`). Props: `mode` (bindable), `hasBlocks` (disables Lesen when false). Emits mode changes.

#### 3b. Modified components

Component | Change
--- | ---
`DocumentTopBar.svelte` | Replace the `Transkribieren` button with `ModeSwitcher`. Keep the `Annotieren` button separate. Add `mode` bindable prop.
`[id]/+page.svelte` | Add `mode` state. Conditionally render `TranscriptionReadView` vs `TranscriptionEditView` in the right panel based on `mode`.
`PdfAnnotationLayer.svelte` | Accept `dimmed` prop. When true: annotation rects get opacity 0.3, no numbered badges, but remain clickable for scroll-sync.

### 4. Read View Rendering

#### 4a. Text rendering

- Fetch transcription blocks from `GET /api/documents/{id}/transcription-blocks` (same endpoint as transcribe mode).
- Render each block as a `<p data-block-id="{block.annotation_id}">` inside an `<article>` element.
- Typography: `font-family: Tinos, Georgia, serif; font-size: 16px; line-height: 1.85`.
- Padding: `24px 32px` for comfortable reading margins.
- `[unleserlich]` markers: detect via regex `/\[unleserlich\]/g` and wrap in `<em class="text-ink-2 italic text-[0.9em]">`.
- Text is not contenteditable. No cursor, no selection highlights, no editing.

#### 4b. Scroll sync

- Each paragraph has a click handler that dispatches a `highlight-annotation` event with the `annotation_id`.
- The PDF viewer listens for this event, scrolls to the annotation, and applies a CSS animation (`flash-fade`, 1.5s ease-out).
- Reverse direction: clicking a dimmed annotation on the PDF dispatches `highlight-paragraph` with the `annotation_id`. The text panel scrolls the matching paragraph into view and applies a background highlight that fades.
- Use `scrollIntoView({ behavior: 'smooth', block: 'center' })` for both directions.
- The highlight CSS animation: `background rgba(0,199,177,.10) → transparent` over 1.5s.

### 5. PDF Annotations in Read Mode

- Turquoise annotation rectangles are rendered but dimmed: border opacity 0.3, background opacity 0.04.
- No numbered badges (the `.ann-num` elements are hidden via `display: none`).
- Annotations remain clickable — they trigger scroll-sync to the matching paragraph.
- When an annotation is flash-highlighted (via scroll-sync), it briefly returns to full opacity before fading back to dimmed.
- Yellow comment annotations are not shown in read mode (they belong to annotate mode only).

### 6. Status Bar

- Positioned at the bottom of the text panel (not the full viewport).
- Content: `"{n} Abschnitte · Zuletzt bearbeitet: {userName}, {HH:mm}"`
- The “Zuletzt bearbeitet” timestamp is the most recent `updated_at` across all transcription blocks for this document.
- The user name comes from the `updated_by` field of that most recently updated block.
- i18n keys: `transcription_status_sections`, `transcription_status_last_edited`.

### 7. Mobile Layout (viewport < 768px)

- The side-by-side split becomes vertical: PDF strip at top, text below.
- PDF strip collapsed height: `70px`. Shows a miniature scan preview.
- Tap strip to expand (~50vh, max 300px). Tap again to collapse. Smooth CSS transition (300ms ease).
- Expand/collapse hint text in bottom-right corner of the strip.
- Mode switcher abbreviation: “Lesen | Bearb.” (i18n key: `mode_edit_short`).
- Scroll-sync on paragraph tap: if PDF is collapsed, auto-expand first, then scroll to annotation.
- Text typography: `15px` (slightly larger than desktop) with `line-height: 1.9`.

### 8. Empty State

- When `transcription_blocks` count is 0, “Lesen” tab is disabled (`opacity: 0.35`, `cursor: not-allowed`, `aria-disabled="true"`).
- Mode defaults to `'transcribe'`.
- Right panel shows empty state: pencil icon in 48px sand circle, title (“Noch keine Transkription”), description (“Zeichne Bereiche auf dem Scan…”).
- As soon as the first block is saved, “Lesen” becomes clickable. Mode does not auto-switch.

### 9. i18n Keys

Key | de | en
--- | --- | ---
`mode_read` | Lesen | Read
`mode_edit` | Bearbeiten | Edit
`mode_edit_short` | Bearb. | Edit
`transcription_status_sections` | {n} Abschnitte | {n} sections
`transcription_status_last_edited` | Zuletzt bearbeitet: {name}, {time} | Last edited: {name}, {time}
`transcription_empty_title` | Noch keine Transkription | No transcription yet
`transcription_empty_desc` | Zeichne Bereiche auf dem Scan und tippe den Text ab, um eine Transkription zu erstellen. | Draw regions on the scan and type the text to create a transcription.
`scan_expand` | Scan vergrößern | Expand scan
`scan_collapse` | Scan verkleinern | Collapse scan

### 10. Accessibility

- Mode switcher: `role="tablist"` with `role="tab"` children, `aria-selected` on active tab.
- Disabled “Lesen” tab: `aria-disabled="true"`, `tabindex="-1"`.
- Read view text: semantic HTML — `<article>` wrapping `<p>` elements. No `contenteditable`.
- Paragraphs are clickable: `role="button"`, `tabindex="0"`, `aria-label="Abschnitt N — klicken um Scan-Position anzuzeigen"`.
- PDF strip toggle on mobile: `role="button"`, `aria-expanded="{expanded}"`, `aria-label="Scan {expanded ? 'verkleinern' : 'vergrößern'}"`.
- Scroll-sync animations respect `prefers-reduced-motion`: skip the 1.5s fade, apply instant highlight that disappears after 200ms.