Briefwechsel — Thumbnail Rows

Final row design for /briefwechsel. PDF thumbnail anchors each row; summary reads as a quote; no status lifecycle, no script-type indicator. Designed for fun discovery, not dense scanning. Scales from 320 px mobile to 1440 px desktop, light and dark. Serves both the millennial audience (25–42) and the senior family audience (60 +) — the senior constraint drives touch targets, line height, and summary legibility.

FINAL
Route
/briefwechsel · list surface
Row height (desktop)
128 px · comfortable
Thumbnail
82×106 portrait · 104×72 landscape
Removed
status dot · script type · archive box
Reading this spec. Mockups in Section 02 are scaled to ~55 % of real pixel values so that multiple viewports fit on one page. Never copy pixel sizes from the mockups. Use the impl-ref tables for exact Tailwind class + pixel value. Close-ups in Section 03 are rendered at ~100 % scale for pixel-accurate reference.
Inhalt
  1. 01 Page anatomy default · 1440 px
  2. 02 Content states × 3 viewports 5 states · 15 frames
  3. 03 Row anatomy close-ups 4 row types @ real size
  4. 04 Distribution bar bilateral mode only
  5. 05 Accessibility contract WCAG AA/AAA
  6. 06 Implementation notes data · thumbnails · routing

01Page Anatomy — Default State at 1440 px (single-person)

The page is a single vertical column (max-w-7xl). Filter card sticks to the top of the content region; the row list starts immediately below, grouped by year dividers. All viewports render the same regions in the same order — they only adapt spacing and thumbnail size, never rearrange.

familienarchiv.de/briefwechsel?senderId=…
DokumentePersonenBriefwechselChronik
Person
Walter de Gruyter
Korrespondent — optional
Alle Korrespondenten
Newest ↓
▾ Filter
851 Briefe
📋 Alle Briefe von Walter de Gruyter — wähle einen Korrespondenten oben um einzugrenzen
19401 Brief
Demo leserlicher Brief
letzte Lebenstage von W. Dörpfeld in Griechenland — ausführlicher Bericht aus Belgard
← eingehendGertrud von Rofden·📍 Belgard·DörpfeldGriechenland
31. Mai 1940
vor 85 Jahren
19235 Briefe
W-0397 – 2. September 1923 – B.Lichterfelde
von Elsbeth geschriebener Kommentar, den Herbert zum Brief erzählte
→ ausgehendan Herbert Cram·📍 B.Lichterfelde·VerlagFamilie
2. September 1923
vor 102 Jahren
Ansichtskarte – 2. September 1923 – B.Lichterfelde
kurze Grüße aus B.Lichterfelde, Hinweis auf den kommenden Besuch
→ ausgehendan Herbert Cram·📍 B.Lichterfelde·✉ Postkarte
2. September 1923
vor 102 Jahren
4 S.
W-0524 – 31. Juli 1923 – Berlin
Glückwunsch zum 60. Geburtstag, Bericht über den Verlag und den Umzug
→ ausgehendan Walter Dieckmann·📍 Berlin·Geburtstag
31. Juli 1923
vor 102 Jahren
A · Filter card
Two inputs (person required, correspondent optional) + action row + hint bar. Uses bg-surface wrapper, not a card — the hint bar gives it closure.
B · Year divider
Sticky-looking band between year groups. Large navy numeral + brief count. Uses bg-muted and a 1 px rule above/below.
C · Row list
Single <ul> per year group. Each row is an <a> with role="listitem" ancestor. Border-left accent colors direction: navy = outgoing, mint-darker = incoming.
Row · Thumbnail cell
Fixed 104 × 120 px cell on desktop; portrait and landscape both centered in the same cell so row height stays consistent across mixed media.
Row · Body
Serif title · italic serif summary (with mint quote glyphs) · sans meta line with direction + counterpart + location + tags. Summary omitted entirely when empty.
Row · Right column
Date (serif, bold) + relative age ("vor 102 Jahren"). No status, no archive location — deliberately calm.
Implementation Reference — Page ShellTailwind 4 · tokens from layout.css
ElementClassesRealNote
Page containermx-auto max-w-7xl px-4 py-8 sm:px-6 lg:px-8max 80remMatches production /briefwechsel
Filter card wrappermb-8 rounded-sm border border-line bg-surface p-6 shadow-smpadding 24 pxExisting CorrespondenzPersonBar container
Year dividerflex items-baseline gap-3 border-y border-line bg-muted px-[14px] py-[8px]border 1 px both sidesKeep production styling — only row changes
Year numeralfont-serif text-2xl font-black tracking-tight text-primary24 px / 900 / -0.025emMerriweather Black
Year counttext-sm font-bold text-ink-314 px / 700"5 Briefe" / Paraglide plural
Row list wrapperoverflow-hidden rounded-sm border border-line bg-surface1 px borderHides row borders at ends
Rowgroup grid grid-cols-[104px_1fr_auto] gap-5 items-center px-5 py-[14px] border-b border-line-2 border-l-[3px] min-h-[128px] cursor-pointer transition-colors hover:bg-muted128 px min · 20 × 14 paddingborder-l-primary out · border-l-accent in
Touch targetFull row is clickable; row height 128 px > WCAG 44 px minimum × ~3128 ≥ 44Senior audience: comfort over density

02Content States × 3 Viewports

Five states covering the combinations that matter. Every frame renders the full page shell (header → filter card → list). Reading order per state: 320 px (mobile S) → 768 px (tablet) → 1440 px (desktop). Watch for filter card wrap at 320, thumbnail shrinkage, and the right-column behaviour under content pressure.

01Default · Single person with mixed row types
The happy path. Four rows shown: incoming typed letter, outgoing handwritten letter, outgoing postcard (landscape thumbnail), outgoing multi-page letter (page badge). Summaries present on three of four — the fourth row shows the clean no-summary variant.
320 px · Mobile176 px @ 55%
9:41
Briefwechsel
Person
Walter de Gruyter
Korrespondent
alle
851
19235
W-0397
Elsbeths Kommentar
H. Cram
2. Sep
Ansichtskarte
H. Cram
2. Sep
4
W-0524
Geburtstag & Umzug
W. Dieckmann
31. Jul
W-0396
H. Cram
2. Sep
768 px · Tablet422 px @ 55%
familienarchiv.de/briefwechsel
DokumenteBriefwechsel
Person
Walter de Gruyter
Korrespondent — optional
Alle Korrespondenten
Newest ↓
▾ Filter
851 Briefe
📋 Alle Briefe von Walter de Gruyter
19235 Briefe
W-0397 – 2. September 1923
von Elsbeth geschriebener Kommentar, den Herbert zum Brief erzählte
an Herbert CramVerlag
2. Sep 1923
vor 102 J.
Ansichtskarte – 2. September 1923
kurze Grüße aus B.Lichterfelde
an Herbert Cram✉ Postkarte
2. Sep 1923
vor 102 J.
4 S.
W-0524 – 31. Juli 1923 – Berlin
Glückwunsch zum 60. Geburtstag, Bericht über den Verlag
an Walter DieckmannGeburtstag
31. Jul 1923
vor 102 J.
W-0396 – 2. September 1923
an Herbert Cram
2. Sep 1923
vor 102 J.
1440 px · Desktop720 px @ 55%
familienarchiv.de/briefwechsel?senderId=…
DokumentePersonenBriefwechselChronik
Person
Walter de Gruyter
Korrespondent — optional
Alle Korrespondenten
Newest ↓
▾ Filter
851 Briefe
📋 Alle Briefe von Walter de Gruyter — wähle einen Korrespondenten oben um einzugrenzen
19235 Briefe
W-0397 – 2. September 1923 – B.Lichterfelde
von Elsbeth geschriebener Kommentar, den Herbert zum Brief erzählte
→ ausgehendan Herbert Cram·📍 B.Lichterfelde·VerlagFamilie
2. September 1923
vor 102 Jahren
Ansichtskarte – 2. September 1923 – B.Lichterfelde
kurze Grüße aus B.Lichterfelde, Hinweis auf den kommenden Besuch
→ ausgehendan Herbert Cram·📍 B.Lichterfelde·✉ Postkarte
2. September 1923
vor 102 Jahren
4 S.
W-0524 – 31. Juli 1923 – Berlin
Glückwunsch zum 60. Geburtstag, Bericht über den Verlag und den Umzug
→ ausgehendan Walter Dieckmann·📍 Berlin·GeburtstagVerlag
31. Juli 1923
vor 102 Jahren
W-0396 – 2. September 1923 – B.Lichterfelde
→ ausgehendan Herbert Cram·📍 B.Lichterfelde
2. September 1923
vor 102 Jahren
Layout-Beobachtungen.
  • 320 px: Filter card collapses to a single column. Title truncates with ellipsis (W-0397), summary keeps 1 line max; counterpart shortens to initials+last (H. Cram). Date format is 2. Sep — no year (year dividers provide it).
  • 768 px: Two-column filter returns. Title shows full label; summary gets 2 lines; date is 2. Sep 1923; location meta omitted (kept to 2 items), tags trimmed to one.
  • 1440 px: Full meta (direction word, counterpart, location, 2 tags). Relative date appears below the absolute date.
  • Row 4 (no summary) retains the exact same row height as others — the row grid is min-h-[128px] at desktop so mixed-summary lists don't visually jump.
02Bilateral · Both filters set + distribution bar
Sender and receiver both selected. A distribution bar appears above the row list, pattern lifted from production ConversationTimeline. Rows show compact direction glyph instead of the word — the bar above already established direction semantics.
320 px · Mobile176 px @ 55%
9:41
Briefwechsel
Person
Walter
Korrespondent
Herbert
143
87 Walter →← 56 Herbert
W-0397
Elsbeths Kommentar
B.Lichterfelde
2. Sep
H-0213
Antwort zur Herbstlieferung
Leipzig
29. Aug
Ansichtskarte
Thür. Wald
20. Aug
768 px · Tablet422 px @ 55%
…/briefwechsel?senderId=&receiverId=
DokumenteBriefwechsel
Person
Walter de Gruyter
Korrespondent
Herbert Cram
⇄ Tauschen
Newest ↓
▾ Filter
143 Briefe
87 von Walter de Gruyter →← 56 von Herbert Cram
W-0397 – 2. September 1923
von Elsbeth geschriebener Kommentar
Walter an HerbertVerlag
2. Sep 1923
vor 102 J.
H-0213 – 29. August 1923 – Leipzig
Antwort auf Walters Anfrage zur Herbstauslieferung
Herbert an WalterVerlag
29. Aug 1923
vor 102 J.
Ansichtskarte – 20. August 1923
Urlaubsgruß aus Thüringen
Herbert an Walter✉ Postkarte
20. Aug 1923
vor 102 J.
1440 px · Desktop720 px @ 55%
familienarchiv.de/briefwechsel?senderId=…&receiverId=…
DokumentePersonenBriefwechselChronik
Person
Walter de Gruyter
Korrespondent
Herbert Cram
⇄ Tauschen
Newest ↓
▾ Filter
143 Briefe im Zeitraum
W-0397 – 2. September 1923 – B.Lichterfelde
von Elsbeth geschriebener Kommentar, den Herbert zum Brief erzählte
Walter an Herbert·📍 B.Lichterfelde·Verlag
2. September 1923
vor 102 Jahren
H-0213 – 29. August 1923 – Leipzig
Antwort auf Walters Anfrage zur Herbstauslieferung
Herbert an Walter·📍 Leipzig·Verlag
29. August 1923
vor 102 Jahren
Ansichtskarte – 20. August 1923 – Thüringer Wald
Urlaubsgruß, kurze Notiz über Wetter und geplante Rückkehr
Herbert an Walter·📍 Thüringer Wald·✉ Postkarte
20. August 1923
vor 102 Jahren
Distribution bar — only renders when both senderId and receiverId are set.
  • Labels are right/left-aligned matching the bar direction (out on left, in on right). Bar widths come from backend-calculated counts, not percentages on the client.
  • role="img" with a descriptive aria-label — screen readers hear the full distribution in one sentence.
  • Below 320 px: labels stack vertically with a 4 px gap. Never truncate a count.
  • In meta line, direction word collapses to glyph ("→ / ←") because the distribution bar above has already named the parties.
03Loading · Skeleton (all three viewports render the same pattern)
SSR renders without thumbnails. While thumbnails are fetching, show a paper-coloured skeleton in the thumbnail cell. Title, summary and meta remain as normal text (the data is already present). No spinner, no pulse on the text — only the thumbnail shimmers.
320 px · Mobile176 px @ 55%
9:41
Person
Walter
Korresp.
alle
851
19235
W-0397
Elsbeths Kommentar
H. Cram
2. Sep
W-0396
H. Cram
2. Sep
768 px · Tablet422 px @ 55%
…/briefwechsel
Person
Walter de Gruyter
Korresp.
Alle
Newest ↓
851 Briefe
19235 Briefe
W-0397 – 2. September 1923
Elsbeths Kommentar
Herbert Cram
2. Sep 1923
W-0396 – 2. September 1923
Herbert Cram
2. Sep 1923
1440 px · Desktop720 px @ 55%
familienarchiv.de/briefwechsel
Person
Walter de Gruyter
Korrespondent
Alle
Newest ↓
▾ Filter
851 Briefe
19235 Briefe
W-0397 – 2. September 1923 – B.Lichterfelde
von Elsbeth geschriebener Kommentar, den Herbert zum Brief erzählte
→ ausgehendan Herbert Cram
2. September 1923
W-0396 – 2. September 1923 – B.Lichterfelde
→ ausgehendan Herbert Cram
2. September 1923
04Empty · No results matching current filters
Filter combination returns zero letters. Empty card sits below the filter card. Primary use case: date range that excludes all letters. Message gives the user a clear reset path.
320 px · Mobile176 px @ 55%
9:41
Person
Walter
Korresp.
alle
0
Keine Briefe
Für diesen Filter gibt es keine Einträge. Zeitraum anpassen oder Filter zurücksetzen.
768 px · Tablet422 px @ 55%
…/briefwechsel?from=1950&to=1960
Person
Walter de Gruyter
Korresp.
Alle
Newest ↓
▾ Filter
0 Briefe
Keine Briefe in diesem Zeitraum
Von 1950 bis 1960 gibt es keine Korrespondenz. Zeitraum erweitern oder Filter zurücksetzen.
1440 px · Desktop720 px @ 55%
familienarchiv.de/briefwechsel?from=1950&to=1960
Person
Walter de Gruyter
Korrespondent
Alle
Newest ↓
▾ Filter
0 Briefe
Keine Briefe in diesem Zeitraum
Von 1950 bis 1960 gibt es keine Korrespondenz mit Walter de Gruyter. Passe den Zeitraum an oder setze die Filter zurück.
05Single-person hint — reminder to narrow
Already shown in production. Stays exactly as is. Re-rendered here so developers confirm it still renders above the first year divider when only senderId is set. Not shown in bilateral mode.
Kein Redesign. Die bestehende SinglePersonHintBar.svelte bleibt unverändert und rendert zwischen Filter-Card und erster Jahres-Trennlinie. Nur in Single-Person-Modus, nicht bilateral.
Implementation Reference — Content Stateslist rendering + skeleton
ElementClassesRealNote
Skeleton thumbanimate-pulse bg-gradient-to-r from-[#f5f4ef] via-[#eceae4] to-[#f5f4ef] rounded-[1px]shimmer 1.4 sApplied only to .bw-thumb, never to text
Empty cardflex flex-col items-center justify-center rounded-sm border border-line bg-muted py-24 text-center shadow-smpadding 96 px yMatches production empty state
Empty titlefont-serif text-ink18 px desktopParaglide: m.conv_no_results_heading()
Empty bodymt-2 text-sm text-ink-3 max-w-prose mx-auto14 pxParaglide: m.conv_no_results_text()
Distribution barflex flex-col gap-1 border-b border-line bg-muted px-[18px] py-2role="img"aria-label: "Briefverteilung: X von A, Y von B"
Distbar labelsflex justify-between text-sm font-bold · .out text-primary · .in text-accent14 px / 700Counts in tabular-nums
Distbar barflex h-[5px] overflow-hidden rounded-full bg-line5 pxSegments animated with transition-[width]

03Row Anatomy · Close-Ups at ~100% Scale

Four row types at near-real pixel sizes. These are the reference renderings developers check against when implementing ConversationTimeline.svelte (or its successor ThumbnailRow.svelte).

Type A · Portrait letter with summary + tags
W-0397 – 2. September 1923 – B.Lichterfelde
von Elsbeth geschriebener Kommentar, den Herbert zum Brief erzählte — Notiz auf der Rückseite
→ ausgehendan Herbert Cram·📍 B.Lichterfelde·VerlagFamilie
2. September 1923
vor 102 Jahren
Type A — Portrait Letter with Summaryrendered from Document + thumbnail URL
PartClassesRealNote
Row containergroup grid grid-cols-[104px_1fr_auto] gap-5 items-center px-5 py-[14px] min-h-[128px] border-b border-line-2 border-l-[3px] border-l-primary transition-colors hover:bg-muted128 px min<a href="/documents/{id}"> · keyboard reachable
Thumbnail cellw-[104px] h-[120px] flex items-center justify-center shrink-0104 × 120Centers any aspect ratio
Thumbnail imgw-[82px] h-[106px] rounded-[1px] shadow-sm ring-1 ring-white/80 transition-transform group-hover:-translate-y-[1px] group-hover:shadow-md82 × 106 portraitloading="lazy" · alt="" (decorative, title covers meaning)
Titlefont-serif text-base font-bold text-ink leading-[1.35] truncate16 px / 700Merriweather Bold
Summaryfont-serif italic text-sm text-ink-2 leading-[1.55] line-clamp-214 px italicOmit element entirely when doc.summary is empty — no placeholder
Summary quote marks::before & ::after pseudos, color text-accent22 px„…" (German curly quotes)
Meta rowmt-0.5 flex flex-wrap gap-x-3 gap-y-1 text-xs text-ink-3 items-center12 pxSeparators use · with text-line
Direction chiptext-[13px] font-extrabold text-primary (out) · text-accent (in)13 px / 800"→ ausgehend" / "← eingehend" (word omitted in bilateral mode)
Tag chipinline-flex items-center text-[10px] font-bold bg-accent/80 text-primary px-[7px] py-0.5 rounded-full10 px / 700Max 2 tags visible at 1440; 1 at 768; 0 at 320
Right column — datefont-serif text-sm font-bold text-ink-2 whitespace-nowrap text-right14 px / 700Intl.DateTimeFormat de-DE (see CLAUDE.md)
Right column — relativetext-[10px] text-ink-3 font-semibold10 px"vor X Jahren" — calculated client-side
Type B · Portrait letter without summary (clean variant)
W-0396 – 2. September 1923 – B.Lichterfelde
→ ausgehendan Herbert Cram·📍 B.Lichterfelde
2. September 1923
vor 102 Jahren
No placeholder when summary is missing. The summary element is not rendered at all — row height still hits min-h-[128px] so the list stays rhythmic. Tags are also omitted when empty (no empty chip row).
Type C · Postcard · landscape thumbnail with stamp + postmark
Ansichtskarte – 20. August 1923 – Thüringer Wald
Urlaubsgruß, kurze Notiz über Wetter und geplante Rückkehr
← eingehendvon Herbert Cram·📍 Thüringer Wald·✉ Postkarte
20. August 1923
vor 102 Jahren
Type C — Postcard (landscape)aspect ratio detection + kind chip
PartClassesRealNote
Thumbnailw-[104px] h-[72px] rounded-[1px] shadow-sm ring-1 ring-white/80104 × 72 landscapeAspect ratio detected server-side from PDF page 1 dimensions (w/h > 1.1 → landscape)
Kind chipinline-flex items-center text-[10px] font-bold uppercase tracking-wide bg-line text-ink-2 px-[7px] py-0.5 rounded-full10 px / 700 uppercaseParaglide: m.doc_kind_postcard() — shown only when thumbnail is landscape
Stamp cornerCSS pseudo-element on thumbnail — 16×18 px gradient square top-right 5 pxdecorativeIn production: rendered by the thumbnail service as part of the real scan; the CSS is only for spec rendering
Type D · Multi-page letter with "N Seiten" badge
4 S.
W-0524 – 31. Juli 1923 – Berlin
Glückwunsch zum 60. Geburtstag, Bericht über den Verlag und den anstehenden Umzug nach B.Lichterfelde
→ ausgehendan Walter Dieckmann·📍 Berlin·GeburtstagVerlag
31. Juli 1923
vor 102 Jahren
Type D — Page-count Badgeonly when pages > 1
PartClassesRealNote
Badge containerabsolute top-1 -right-1 bg-primary text-primary-fg text-[10px] font-bold px-[7px] py-0.5 rounded-full ring-2 ring-white10 px / 700Overlaps the thumbnail by 4 px right
LabelParaglide: m.doc_pages_count({ count })"4 S."Abbreviated form for the badge; full "4 Seiten" appears in the document detail page
Visibility ruleRender {#if doc.pageCount > 1}Never show "1 S."

04Distribution Bar · Close-Up

Only rendered in bilateral mode (both senderId and receiverId set). This component already exists in production as part of ConversationTimeline.svelte — this spec keeps its API and visual treatment identical but moves it out of the timeline header into a standalone component above the row list, so it can sit between the filter card and the year dividers.

Distribution bar · bilateral Walter ↔ Herbert
87 von Walter de Gruyter → ← 56 von Herbert Cram
Distribution Barrole="img" + aria-label carries the data
PartClassesRealNote
Wrapperflex flex-col gap-1 border-b border-line bg-muted px-[18px] py-28 px y paddingrole="img" · aria-label describes full distribution
Out labelinline-flex items-center gap-1 text-primary text-sm font-bold tabular-nums14 px / 700Format: "{count} von {sender} →"
In labelinline-flex items-center gap-1 text-accent text-sm font-bold tabular-nums14 px / 700Format: "← {count} von {receiver}"
Barflex h-[5px] overflow-hidden rounded-full bg-line5 px tallSegments use transition-[width] duration-300 ease-out
Out segmentbg-primary h-fullwidth from APIPercentage computed backend-side from counts
In segmentbg-accent h-fullcomplementaryNever use 100% - out; both come from the API separately
Mobile (320 px)Labels stack with flex-col gap-1; bar stays full-widthNo truncation of counts — numbers must always be legible

05Accessibility Contract · WCAG AA/AAA

Every colour pair on the rendered row has been measured. AAA where reasonably achievable; AA is the floor. The row is a link, not a button — keyboard navigation is native tab-through-list semantics.

Light Mode — Contrast Verificationlayout.css tokens
PairValueRatioWCAG
Title (ink on surface)#1A1A1A on #ffffff19.6:1AAA ✓
Summary (ink-2 on surface)#444444 on #ffffff9.7:1AAA ✓ (body)
Meta (ink-3 on surface)#666666 on #ffffff5.7:1AA ✓
Direction out (primary on surface)#002850 on #ffffff14.5:1AAA ✓
Direction in (accent on surface)#2F9E95 on #ffffff4.6:1AA ✓ (normal)
Tag chip (primary on mint)#002850 on #a6dad88.1:1AAA ✓
Quote marks (accent on surface)#a6dad8 decorativen/aDecorative — summary text carries meaning
Focus ring (primary on surface)#002850 on #ffffff, 2px offset14.5:1AAA ✓
Dark Mode — Contrast Verificationremap via data-theme="dark"
PairValueRatioWCAG
Title (ink on surface-dark)#f0efe9 on #011a3015.1:1AAA ✓
Summary (ink-2 on surface-dark)#c5cbd4 on #011a3011.2:1AAA ✓
Meta (ink-3 on surface-dark)#9ca3af on #011a307.8:1AAA ✓ (body)
Direction out (mint on canvas)#a1dcd8 on #010e1e9.6:1AAA ✓
Direction in (turquoise on canvas)#00c7b1 on #010e1e6.8:1AA ✓
Tag chip (turquoise on tint)#00c7b1 on rgba(0,199,177,.2)6.3:1AA ✓
Non-negotiable accessibility rules.
  • Row is rendered as <a href="/documents/{id}"> — never <div onclick>. Keyboard Tab enters, Enter opens, Shift-Tab leaves.
  • Focus ring: focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 — always visible on keyboard focus, never on mouse click.
  • Thumbnail <img alt=""> — empty alt because the title next to it names the letter. A descriptive alt would be announced twice.
  • Direction glyph is color and shape (arrow direction). Never rely on color alone — the arrow "→" vs "←" carries meaning even in monochrome.
  • Distribution bar uses role="img" with a full-sentence aria-label. Screen readers hear the whole distribution in one announcement, not each half.
  • Minimum body text 14 px; minimum meta text 12 px. Never below 12 px for any visible text.
  • Touch target: 128 px row height ≫ 44 px WCAG minimum. Comfortable for senior users on phones.
  • prefers-reduced-motion: hover lift on thumbnail collapses to transition-duration: 0.01ms. Required (project CLAUDE.md + WCAG 2.3.3).

06Implementation Notes — Data, Thumbnails, Routing

Data contract — fields read per row/api/documents/conversation
FieldFromUsed forFallback
idDocumentRow key, hrefrequired
titleDocumentRow titleoriginalFilename
summaryDocumentQuote line (omit when empty)element not rendered
documentDateDocumentYear group, right-column date, relative time"—" placeholder, year group "Ohne Datum"
locationDocumentMeta linehidden
sender / receiversDocumentDirection + counterpart namedirection omitted, name = m.conv_no_party()
tags[]DocumentMeta line (max 2 at 1440, 1 at 768, 0 at 320)no chips rendered
pageCountDocument (new, from thumbnail service)Badge when > 1no badge
thumbnailUrlDocument (new, from thumbnail service)<img src>skeleton until fetched
thumbnailAspectDocument (new, from thumbnail service)portrait / landscape classdefaults to portrait
Thumbnail service — new endpointsdepends on open issue "thumbnail generation"
ConcernDecisionNote
StorageMinIO bucket thumbnailsMirrors document ID path; WEBP at 2× target resolution
URL/api/documents/{id}/thumbnailRedirects (302) to a presigned MinIO URL · Cache-Control: public, max-age=2592000 (30 d)
AspectComputed once on generation, persisted as Document.thumbnailAspect enum PORTRAIT \| LANDSCAPEThreshold w/h > 1.1 → LANDSCAPE
Page countPersisted as Document.pageCount on upload / reprocessNot computed client-side
Loading strategy<img loading="lazy" decoding="async"> with intersection observer for rows below the foldSkeleton state until onload fires
FallbackPaper-coloured placeholder (matches thumbnail gradient) with document iconNever break the row layout
Component structurenew files
FileResponsibilityReplaces
ThumbnailRow.svelteSingle row with thumbnail, title, summary, meta, right columnRow rendering inside ConversationTimeline.svelte
DistributionBar.svelteThe bilateral distribution barLifts existing markup out of ConversationTimeline.svelte
YearDivider.svelteYear number + Briefe countAlready exists; no change required
ConversationTimeline.svelteOrchestrator · renders distribution bar + year dividers + ThumbnailRowsSimplified — no longer does row markup directly
DocumentThumbnail.svelteReusable thumbnail element with lazy-load + aspect + page badgenew · also usable on /documents list pages
Shipping order.
  • Phase 1 — land ThumbnailRow, DistributionBar (extracted), and new typography/spacing without real thumbnails. Thumbnail cell renders the skeleton permanently. Ship and observe.
  • Phase 2 — wire up thumbnail service (open issue "PDF thumbnail generation"). Replace skeleton with real thumbnails. Add thumbnailAspect + pageCount to the Document entity and the /api/documents/conversation response.
  • Phase 3 — add lazy-loading + intersection observer for rows outside viewport. Measure perf on 851-letter lists.