Redesign of the homepage (/) as an action-led document hub. Replaces the wall-of-lists approach with a warm, collaborative dashboard: personal greeting, hero "resume" card, Mission Control 3-up, Family Pulse (this week only), activity feed, and sidebar dropzone. Variant A selected from a three-concept exploration. Addresses Issue #271.
| Element | Tailwind classes | Real px | Notes |
|---|---|---|---|
| Page background | min-h-screen bg-canvas | — | Canvas = #f0efe9 |
| Main content wrapper | max-w-screen-xl mx-auto px-8 py-8 | 32px padding, 1280px max | — |
| 2-column layout grid | grid grid-cols-[1fr_320px] gap-5 items-start | 20px gap, 320px sidebar | Collapses to 1-col on mobile |
| Main column | flex flex-col gap-5 | 20px gap between sections | — |
| Sidebar | flex flex-col gap-5 | 20px gap | Sticky on tall viewports: sticky top-[80px] (below header) |
| Element | Tailwind classes | Real px | Notes |
|---|---|---|---|
| Header | sticky top-0 z-50 bg-[#012851] | — | Already implemented — no change needed |
| Mint stripe | h-1 bg-accent | 4px | Already implemented |
| Nav bar height | h-16 flex items-center px-8 | 64px | Already implemented |
| Upload button (new) | inline-flex items-center gap-2 border border-white/25 text-white font-sans text-[11px] font-bold tracking-[.12em] uppercase px-3.5 py-1.5 rounded-sm hover:bg-white/10 transition-colors | 11px font | New addition to existing header. Routes to /documents/new or triggers upload modal. |
| User avatar | w-8 h-8 rounded-full bg-white text-ink font-sans text-[11px] font-bold flex items-center justify-center | 32px | Shows user initials. Click → user menu (out of scope for this issue). |
| Element | Tailwind / implementation | Real px | Notes |
|---|---|---|---|
| Greeting wrapper | mb-5 | 20px bottom margin | — |
| Greeting headline | font-serif font-normal text-[32px] leading-tight text-ink | 32px | Time-of-day: getHours() < 11 → m.greeting_morning(). Uses logged-in user's first name from data.currentUser.firstName. |
| Greeting subtitle | font-serif text-[17px] text-ink-2 mt-2 leading-[1.45] | 17px | Driven by actual notification data. Priority: @mention > reply > new scan. Links to document. Falls back to a neutral welcome if no recent activity. |
| Search bar card | bg-surface border border-line shadow-sm rounded-sm p-5 mb-6 flex items-center gap-3 | 20px padding, 24px bottom margin | Same component as existing SearchFilterBar.svelte — no change needed. |
| Search input | flex-1 border border-line px-3.5 py-3 font-serif text-base text-ink placeholder:text-ink-3 rounded-none focus-visible:ring-2 focus-visible:ring-focus-ring/15 focus-visible:border-ink | 16px font / 48px tall | Already implemented in SearchFilterBar. |
| Element | Tailwind classes | Real px | Notes |
|---|---|---|---|
| Hero card | bg-surface border border-line shadow-sm rounded-sm p-7 grid grid-cols-[180px_1fr] gap-8 items-center | 28px padding, 32px gap | Collapses to single-column on mobile: sm:grid-cols-[180px_1fr] |
| Letter thumbnail | w-[180px] h-[246px] rounded-sm shadow-sm flex-shrink-0 | 180×246px | Real scan image when available; SVG placeholder when not. <img> with object-cover. |
| Eyebrow label | inline-flex items-center gap-2 font-sans text-[11px] font-bold tracking-[.18em] uppercase text-turquoise | 11px | Dot: w-2 h-2 rounded-full bg-turquoise. i18n: m.dashboard_resume_label() |
| Page counter | font-sans text-[11px] tracking-[.12em] uppercase text-ink-3 | 11px | m.dashboard_page_of({ page, pages }) |
| Letter title | font-serif font-normal text-[30px] leading-tight text-ink mb-1.5 | 30px | From document.title |
| Archival caption | font-serif text-sm italic text-ink-3 mb-4 | 14px | "{from} an {to} · {place}, {date}" |
| Excerpt pull-quote | font-serif text-[17px] italic leading-[1.55] text-ink-2 border-l-[3px] border-accent pl-4 mb-5 | 17px, 3px left border (mint) | First 200 chars of transcription draft or OCR excerpt. |
| Progress row | flex justify-between items-center font-sans text-xs text-ink-3 mb-2 | 12px | Left: "{pct}% transkribiert". Right: avatar stack + collaborator names. |
| Progress bar | w-full h-1.5 bg-[#eeede8] rounded-full overflow-hidden mb-5 | 6px | Fill: bg-turquoise (#00c7b1). This is per-letter (clear denominator) — motivating, not crushing. |
| Primary CTA | bg-primary text-primary-fg font-sans text-xs font-bold tracking-[.12em] uppercase px-5 py-3 rounded-sm inline-flex items-center gap-2.5 | 12px | href="/documents/{id}/edit". i18n: m.dashboard_resume_cta() |
| "oder anderen Brief" link | font-sans text-xs font-semibold text-ink-2 underline decoration-[#cdcbbf] underline-offset-[4px] | 12px | href="/" (to search). Not a button — no prominent treatment. |
| Empty state | Centered card with envelope icon, serif heading, body, CTA button | — | Shown when backend returns no active transcription for current user. |
| Element | Tailwind classes | Real px | Notes |
|---|---|---|---|
| 3-column grid | grid grid-cols-3 gap-4 | 16px gap | Collapses to 1-col on mobile |
| Column card | bg-surface border border-line rounded-sm overflow-hidden flex flex-col | — | — |
| Column header — Segmentieren | p-4 border-b border-[#eeede8] bg-[rgba(160,82,45,.06)] | 16px padding | Sienna tint |
| Column header — Transkribieren | bg-[rgba(0,199,177,.08)] | — | Turquoise tint |
| Column header — Prüfen | bg-[rgba(90,138,106,.08)] | — | Sage tint |
| Column label | font-sans text-[12px] font-bold tracking-[.16em] uppercase text-ink | 12px | i18n: m.queue_segment(), m.queue_transcribe(), m.queue_review() |
| Open count | font-sans text-[11px] text-ink-3 | 11px | "{n} offen" |
| Column blurb | font-serif text-[13px] text-ink-2 leading-snug mt-1.5 | 13px | i18n keys: m.queue_segment_blurb() etc. |
| Task row wrapper | p-1.5 | 6px | Padding around all rows within the column body. |
| Task row | <a> flex items-center gap-3 px-3.5 py-2.5 rounded-sm hover:bg-[#f7f6f1] transition-colors no-underline | 14px/10px padding | Entire row is a link to the document edit page. |
| Task title | font-serif text-base text-ink truncate leading-snug | 16px | Single line, truncated with ellipsis. |
| Task metadata | font-sans text-[12px] text-ink-3 mt-0.5 flex items-center gap-2 | 12px | Hint · separator · pages or % or "fertig". "fertig" = text-[#5a8a6a] font-semibold. |
| Starter avatar (filled) | w-[22px] h-[22px] rounded-full flex-shrink-0 | 22px | Uses person's color from the persons list. |
| Starter avatar (empty) | w-[22px] h-[22px] rounded-full border-[1.5px] border-dashed border-[#cdcbbf] flex-shrink-0 | 22px | Title attribute: "noch niemand angefangen" (tooltip). |
| "Alle anzeigen" footer | text-center py-2.5 font-sans text-[11px] font-bold tracking-[.12em] uppercase text-ink border-t border-[#eeede8] mt-auto block | 11px | Links to filtered document list for that queue. |
| Element | Tailwind classes | Real px | Notes |
|---|---|---|---|
| Card | bg-surface border border-line shadow-sm rounded-sm p-6 | 24px padding | White card — intentionally same as other sidebar cards (no warm cream — confirmed decision). |
| Eyebrow | font-sans text-[11px] font-bold tracking-[.18em] uppercase text-ink-3 mb-2 | 11px | i18n: m.pulse_eyebrow() = "Diese Woche · gemeinsam" |
| Headline | font-serif text-[22px] font-normal leading-snug text-ink mb-1 | 22px | i18n: m.pulse_headline({ pages }). Strong tag on the number: font-semibold. |
| Personal contribution | font-serif text-[14px] text-ink-3 mb-4 | 14px | i18n: m.pulse_you({ pages }). Hidden when current user's contribution = 0. |
| Avatar stack | inline-flex items-center with -ml-2 on all except first | 28px avatars, -8px overlap | Shows contributors who participated this week. ring-2 ring-white on each. |
| Contributor count | font-sans text-xs text-ink-3 ml-3 | 12px | "{n} Mitwirkende" |
| 3-stat grid | grid grid-cols-3 gap-2.5 mt-4 | 10px gap, 16px top margin | — |
| Stat card | bg-[#faf9f4] border border-[#eeede8] rounded-sm p-3.5 | 14px padding | — |
| Big number | font-serif text-[30px] font-normal text-ink leading-none flex items-baseline gap-2 | 30px | Dot: w-1.5 h-1.5 rounded-full flex-shrink-0 in kind color. |
| Stat label | font-sans text-[12px] text-ink-3 mt-1.5 | 12px | i18n: m.pulse_transcribed(), m.pulse_reviewed(), m.pulse_uploaded() |
| Data source | — | — | New backend endpoint: GET /api/dashboard/pulse?period=week. Returns { transcribed, reviewed, uploaded, pages, you: { pages }, contributors: Person[] }. |
| Element | Tailwind classes | Real px | Notes |
|---|---|---|---|
| Feed card wrapper | bg-surface border border-line shadow-sm rounded-sm p-5 | 20px padding | — |
| Feed item | flex gap-3.5 items-start py-3.5 border-b border-[#eeede8] last:border-b-0 | 14px padding, 14px gap | — |
| Feed avatar | w-9 h-9 rounded-full flex-shrink-0 | 36px | Color from person's color field. |
| Feed text | font-serif text-[15px] leading-snug text-ink | 15px | Name: font-sans text-[13px] font-bold. Verb: text-ink-2. Target: link text-ink underline underline-offset-[2px]. |
| "für dich" badge | ml-2 inline-block px-2 py-px border border-accent rounded-full font-sans text-[10px] font-bold tracking-[.12em] uppercase text-ink bg-accent/20 align-middle | 10px | Only shown when activity.you === true (mention or reply to current user). |
| Timestamp row | mt-1 inline-flex items-center gap-2 font-sans text-[11px] text-ink-3 | 11px | Kind dot: w-1.5 h-1.5 rounded-full in kind color (see dot-color map below). |
| Kind → dot color | transcribe: #00c7b1 · segment: #a0522d · review: #5a8a6a · upload: #3060b0 · mention: #c0446e · reply: #7a4f9a · tag: #c17a00 | — | Purely decorative — color-blind safe because avatar + name already identify the person and action. |
| Dropzone — idle | flex flex-col items-center gap-2.5 py-6 px-4 border border-dashed border-ink/30 rounded-sm text-center transition-all | — | Already implemented in DropZone.svelte. Already existing; verify styles match. |
| Dropzone — drag active | + border-ink bg-accent/18 | — | Title changes to m.dropzone_release(). |
| Data source — feed | — | — | New endpoint: GET /api/dashboard/activity?limit=7. Returns recent actions across all document activity for the archive. |
| Endpoint | Response | Notes |
|---|---|---|
GET /api/dashboard/resume | { document, page, pages, pct, collaborators: Person[] } | The current user's last-touched in-progress transcription. Returns null when none. |
GET /api/dashboard/pulse?period=week | { transcribed, reviewed, uploaded, pages, you: { pages, comments }, contributors: Person[] } | Aggregate stats for the current ISO week. Never exposes cumulative total. |
GET /api/dashboard/activity?limit=7 | Activity[] — { who: Person, verb, target, targetId, when, kind, youMentioned } | Recent archive-wide activity, newest first. youMentioned=true when activity targets the current user. |
| Endpoint | Used for |
|---|---|
GET /api/documents?status=UPLOADED&transcriptionPct=0..99&sort=DATE&limit=5 | Transkribieren queue |
GET /api/documents?status=PLACEHOLDER&limit=5 | Segmentieren queue |
GET /api/documents?status=TRANSCRIBED&limit=5 | Prüfen queue |
GET /api/documents?q=&sort=DATE&dir=desc | Search bar (unchanged) |
| Key | DE |
|---|---|
greeting_morning | Guten Morgen, {name}. |
greeting_day | Hallo, {name}. |
greeting_evening | Guten Abend, {name}. |
dashboard_resume_label | Weiter, wo du aufgehört hast |
dashboard_page_of | Seite {page} von {pages} |
dashboard_resume_cta | Weitertranskribieren |
dashboard_resume_other | oder anderen Brief wählen |
dashboard_empty_title | Wo möchtest du anfangen? |
dashboard_empty_body | Wähle einen Brief aus einer Warteschlange unten oder durchsuche das Archiv. |
dashboard_mission_caption | Mitmachen |
queue_segment | Segmentieren |
queue_segment_blurb | Seiten zu Briefen zuordnen. |
queue_transcribe | Transkribieren |
queue_transcribe_blurb | Alte Handschriften lesen und abtippen. |
queue_review | Prüfen |
queue_review_blurb | Letzte Korrektur und freigeben. |
queue_n_open | {n} offen |
queue_show_all | Alle anzeigen → |
pulse_eyebrow | Diese Woche · gemeinsam |
pulse_headline | Ihr habt zusammen {pages} Seiten bearbeitet. |
pulse_you | Du selbst hast {pages} davon transkribiert. |
pulse_contributors | {n} Mitwirkende |
pulse_transcribed | Briefe transkribiert |
pulse_reviewed | Briefe geprüft |
pulse_uploaded | Seiten hochgeladen |
feed_caption | Kommentare & Aktivität |
feed_for_you | für dich |
dropzone_release | Loslassen zum Hochladen |
GET /api/dashboard/resume, /pulse, /activity endpoints to Spring Bootnpm run generate:api)routes/+page.server.ts to load all dashboard data in parallelDashboardResumeStrip.svelte — add thumb, caption, pull-quote, progress bar, new CTAsMissionControlStrip.svelte — 3-col grid, TaskRow with avatar-of-starter, accent headersDashboardFamilyPulse.svelte (new)DashboardActivityFeed.svelte (new) — replaces DashboardRecentDocuments+page.svelte+layout.svelte header