UX Spec · Bulk Upload

Uploading multiple documents in a single pass

Extends issue #294 (new-document split-panel) with bulk uploads. When a user drops N files, every metadata field applies once to all of them — only the title is per-file, pre-filled from the filename and editable inline. A single save POST creates N documents.

feature ui a11y 320px+ backend ready

Design goals

A
Concept A
Flat Stack — shared header · file cards · sticky save

A single vertical flow: drop zone on top, then a Gilt für alle metadata card, then stacked file cards (thumbnail · editable title · remove). No split panel, no tabs. Scrolling down reveals all files; the save bar sticks to the bottom.

Best for Small-screen workflows. Seniors who prefer linear flows over tabs.
Trade-off: no PDF preview until you click through to the document after save. Harder to verify you grabbed the right files before committing.
375 · mobile
MR
← Zurück
Neue Dokumente
5
Weitere Dateien hinzufügen
PDF, JPEG, PNG, TIFF · max 50 MB
Gilt für alle 5 Angaben
Absender
Hans Müller
Empfänger
Anna Schmidt
Datum
1950-06
Ort
Berlin
Tags
Familie × Krieg ×
5 Dateien · Titel bearbeiten
Brief_1940_Hans
Brief_1940_Hans.pdf · 2.4 MB
Brief_1940_Anna
Brief_1940_Anna.pdf · 1.8 MB
Brief_1941_Clara
Brief_1941_Clara.pdf · 890 kB
Postkarte_Venedig
Postkarte_Venedig.jpg · 1.1 MB
Alle verwerfen
Als Platzhalter
5 speichern →
B
Concept B · RECOMMENDED
Split-Panel with File Switcher

Reuses the DocumentEditLayout from issue #294 and adds a horizontal file-switcher strip under the PDF preview. Right column splits into two cards: Gilt nur für diese Datei (title only, mint accent) and Gilt für alle N Dokumente (everything else). When N=1 the switcher disappears and the screen is byte-identical to #294.

Best for The project's primary use case. Desktop + tablet, matches #294 DNA.
Trade-off: on mobile the split has to collapse into tabs ("Vorschau / Angaben"). We reuse the same responsive pattern that DocumentEditLayout already ships with.
1280 · desktop
MR
← Dokumente
Neue Dokumente
5 werden erstellt
Alle verwerfen
+
Seite 1 / 2 · Datei 1 von 5
1 Brief_1940_Hans.pdf
2 Brief_1940_Anna.pdf
3 Brief_1941_Clara.pdf
4 Postkarte_Venedig.jpg
5 Urkunde_1942.pdf
Nur diese Datei 1 / 5 · Brief_1940_Hans.pdf
Titel *
Brief an Anna, 1940
Gilt für alle 5 Gemeinsame Angaben
Absender
Hans Müller
Empfänger
Anna Schmidt
Datum
15.06.1950
Ort
z.B. Berlin
Tags
Familie × Krieg × Briefwechsel ×
Archivbox
z.B. B-12
Mappe
z.B. M-3
Alle verwerfen
Als Platzhalter
5 speichern →
C
Concept C
Progressive Accordion — shared sticky header · file cards expand inline

Shared metadata sticks at the top of the page. Below, each file is a collapsed card; clicking a card expands it to show the PDF preview + title field inline. Only one card is expanded at a time. Scales well to 20+ files — the list stays readable, you only look at the PDFs you want to verify.

Best for Large batches (10+ files) where you want to spot-check a few.
Trade-off: two different visual languages — cards collapsed vs. cards expanded with PDF. New pattern for the project; costs familiarity.
1280 · desktop
MR
← Zurück
Neue Dokumente
5
Gilt für alle 5 Gemeinsame Angaben
Absender
Hans Müller
Empfänger
Anna Schmidt
Datum
15.06.1950
Tags
Familie ×Krieg ×
Ort
z.B. Berlin
5 Dateien
Brief an Anna, 1940
Brief_1940_Hans.pdf · 2.4 MB
Brief von Anna, Antwort
Brief_1940_Anna.pdf · 1.8 MB
Nur diese Datei 2 / 5
Titel *
Brief von Anna, Antwort
Brief_1941_Clara
Brief_1941_Clara.pdf · 890 kB
Postkarte_Venedig
Postkarte_Venedig.jpg · 1.1 MB
Urkunde_1942
Urkunde_1942.pdf · 3.1 MB
Alle verwerfen
Als Platzhalter
5 speichern →

Decision matrix

All three concepts meet the core requirement (shared metadata + per-file title + one save). Graded against what matters for the senior audience, the responsive constraint, and the #294 architectural commitment.

Dimension A · Stack B · Split-Panel C · Accordion
Reuses #294 layout
Single-file mode unchanged rewrite identical different
PDF visible before save no always one at a time
Works at 320px native via tab collapse native
Scales to 20 files long scroll switcher scrolls collapsed list
New Svelte components 3 new 1 new (switcher) 4 new
Familiar pattern yes yes (post-#294) new to app
Recommendation

Ship Concept B

Concept B treats bulk upload as a polymorphic state of the existing single-document layout rather than a separate screen. A user who drops one file gets exactly the #294 experience. A user who drops five gets the same screen plus a horizontal file-switcher and a two-card split (Nur diese Datei vs. Gilt für alle). Nothing about the single-file flow changes.

Implementation reference — Concept B

Top bar (when N > 1)

ElementTailwindPx / valueNote
Count pill "N werden erstellt" bg-accent text-primary rounded-full px-3 py-1 text-sm font-bold 14px · 700 brand-mint on brand-navy
"Alle verwerfen" link ml-auto text-sm font-bold text-red-600 hover:text-red-800 focus-visible:outline-2 focus-visible:outline-red-600 14px / 44px target confirm dialog before wiping

FileSwitcherStrip (new component)

ElementTailwindPx / valueNote
Strip container flex items-center gap-1 bg-ink/95 px-2 py-2 border-t border-ink/80 height 48px under the PDF toolbar, on the dark panel
Arrow buttons h-10 w-10 rounded-sm bg-white/8 text-surface/60 hover:bg-white/15 focus-visible:outline-2 40×40 (44 w/padding) aria-label="Vorherige Datei" / "Nächste Datei"
File chip (inactive) px-3 py-2 rounded-sm bg-white/6 text-sm font-bold text-surface/55 whitespace-nowrap hover:bg-white/12 14px / h 40px horizontal scroll container uses snap-x snap-mandatory
File chip (active) ... bg-accent text-primary + aria-current="true" 14px / h 40px mint pill, primary text — 7.2:1 contrast passes AAA
Chip number prefix bg-primary/25 rounded-sm px-1 mr-2 text-xs font-extrabold 12px / 800 "1", "2", … — for quick scanning

"Nur diese Datei" card (per-file scope)

ElementTailwindPx / valueNote
Card container bg-accent/20 border border-accent rounded-sm p-4 mb-4 padding 16px mint tint signals "different per file"
Scope badge bg-primary/90 text-accent rounded-sm px-2 py-1 text-xs font-extrabold uppercase tracking-wide 12px · 800 Paraglide key: bulk_only_this_file
Title input h-11 text-base font-semibold text-ink bg-white border border-line rounded-sm px-3 focus-visible:border-ink focus-visible:ring-2 focus-visible:ring-ink/20 44px min-height · 16px pre-filled from filename without extension

"Gilt für alle" card (shared scope)

ElementTailwindPx / valueNote
Card container bg-surface border border-line rounded-sm p-4 mb-3 padding 16px neutral (no accent tint)
Scope badge bg-accent text-primary rounded-sm px-2 py-1 text-xs font-extrabold uppercase tracking-wide 12px · 800 Paraglide: bulk_shared_count ("Gilt für alle {count}")
Field grid grid grid-cols-1 md:grid-cols-2 gap-3 12px gap single column at 320px, two at ≥ 768px

Save bar

ElementTailwindPx / valueNote
Primary save button h-11 px-5 bg-green-700 hover:bg-green-800 text-white font-extrabold rounded-sm text-sm focus-visible:ring-2 focus-visible:ring-green-900 44px min · 14px label {count} speichern → (plural-aware Paraglide)
"Als Platzhalter" (outline) h-11 px-4 border border-line bg-white text-ink-3 font-bold rounded-sm text-sm 44px posts with metadataComplete=false for all

Responsive collapse (≤ 767px)

ElementTailwindPx / valueNote
Panel mode switch reuses DocumentEditLayout's existing tab collapse — "Vorschau / Angaben" tabs tab height 48px already shipped with #294
File switcher stays on "Vorschau" tab snap-x snap-mandatory overflow-x-auto h 44px horizontal swipe; arrow buttons removed at mobile
Interactions + behaviour
  • Drop a file after the initial batch: append to the end of the list and switch focus to the newly added file. No modal, no confirmation.
  • Remove a file (X on the chip) → confirm only if it's the currently-previewed one; otherwise silent. When count drops to 1 the switcher strip animates away (200ms); when it drops to 0 we redirect back to the drop-zone state.
  • Title auto-fill: filename.replace(/\.(pdf|jpe?g|png|tiff?)$/i, '').replace(/[_-]+/g, ' ').trim(). Marks the title input as suggested until the user edits it (mint left border, same treatment as #294's filename-derived fields).
  • Title field visibility: always rendered (never collapsed) even in single-file mode, so there's zero layout jump when N changes from 1 to 2.
  • Save flow: single POST to /api/documents/quick-upload with N files + JSON metadata object containing shared fields + titles array. Backend maps title[i] to files[i] by index. Response splits into created[] / updated[] / errors[] — show a summary toast + inline error markers per file for the errors[] list.
  • Keyboard navigation: / on the switcher strip moves file focus; Tab cycles through form fields inside whichever card is active; Esc on the discard button opens the confirm dialog.
  • Focus management on file switch: when the user clicks a different file, the title input of the new file receives focus automatically (so the main editable field is always reachable).
  • Progress indicator during save: replace the save button with a determinate progress bar showing "Lade Datei 3 von 5…" for batches that take > 500ms.
Edge cases + a11y
  • Duplicate filenames in the batch: accept, but show a warning icon next to both — backend will create both with unique IDs.
  • Mixed content types: PDF + image in the same batch is fine; the preview panel renders whichever the active file is (DocumentEditLayout already handles both).
  • Large batches (> 20 files): the switcher strip becomes scrollable; consider a "Jump to file…" combobox at > 30 files (out of scope for v1).
  • Upload failure per file: mark the chip red (bg-red-600/20 text-red-800 border border-red-600), show inline error in the chip's tooltip, don't block the rest of the batch from retrying.
  • Screen reader announcement: when file count changes, fire a polite live region announce — "5 Dateien bereit zum Speichern" via role="status" aria-live="polite".
  • Colour-alone warning: active file chip uses color + aria-current="true" + a ▸ caret prefix so it's distinguishable for color-blind users.