Files
familienarchiv/design_handoff_familienarchiv_redesign/EPIC.md
marcel fa510f3991
Some checks failed
CI / Unit & Component Tests (push) Failing after 2m1s
CI / OCR Service Tests (push) Successful in 28s
CI / Backend Unit Tests (push) Successful in 6m21s
CI / fail2ban Regex (push) Successful in 44s
CI / Semgrep Security Scan (push) Successful in 27s
CI / Compose Bucket Idempotency (push) Successful in 1m8s
nightly / deploy-staging (push) Successful in 4m43s
nightly / npm-audit (push) Failing after 19s
Renovate / renovate (push) Failing after 38s
feat(redesign): token foundation close-out + design handoff (#854)
Adds the Mappe design handoff as in-repo ground truth and closes out the design-token foundation: --radius-sm/md/full + --shadow-sm/md tokens (distinct dark values in both dark blocks) and the canonical 10-color $lib/shared/avatarPalette.ts (AA-darkened sage/amber/sand swatches), guarded by avatarPalette.spec.ts. Closes #854.
2026-06-16 17:17:27 +02:00

218 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# EPIC: Familienarchiv Visual Redesign ("Mappe")
> Implement **in order**. Stories 14 are foundation and shared primitives — every screen
> depends on them. Do not start a screen story until 14 are merged, or the header, avatar,
> and tokens get re-implemented per screen and drift. Read `DESIGN_RULES.md` first; open the
> matching `prototypes/*.dc.html` for pixel ground truth while building each story.
## Epic goal
Reskin the entire Familienarchiv app to the unified "Mappe" archival direction and ship
three new sections (Geschichten, Zeitstrahl, Aktivitäten). All copy German-first via
Paraglide; light + dark mode; De Gruyter icon convention preserved.
## Epic-level acceptance criteria
- [ ] All eight screens match their prototype in light **and** dark mode.
- [ ] Header, page-header, avatar, segmented control, and card exist as **single shared
components** — zero duplication of the header markup or the avatar-color function.
- [ ] No raw hex in components; everything references the semantic tokens.
- [ ] Every visible string is a Paraglide message key, German authored first; `en`/`es`
stubs added.
- [ ] Icons rendered as `<img>`, invert correctly in dark mode.
- [ ] No gradients, blur, emoji, or transform-based motion introduced.
---
> **Reframe (2026-06-16): this is alignment, not greenfield.** An audit of the live codebase
> found the substrate already in place — the full token system (`DESIGN_RULES §12`), dark
> mode, the app header, and the three "new" sections (Geschichten, Zeitstrahl, Aktivitäten)
> all already exist. So **Stories 14 below are close-out + extraction**, not from-scratch
> builds, and Stories 511 are "align the existing screen to its prototype," not new pages.
> The detailed, trackable breakdown lives in the Gitea milestone **"Mappe Visual Redesign"**
> (issues split into *shared components* then *pages*); **Phase B** at the bottom of this file
> lists the screens that previously had no prototype and now do.
## Story 1 — Foundation: tokens, fonts, theme
**Goal:** establish the visual substrate the whole app reads from.
- Port `prototypes/colors_and_type.css` into the app's token layer (Tailwind 4 `@theme` /
`layout.css`). Keep every variable name in `DESIGN_RULES.md §1`.
- Wire Montserrat + Tinos (or licensed Gotham/Times) and the `--font-sans`/`--font-serif` vars.
- Implement light/dark via `:root[data-theme='dark']` + the pre-paint boot script that reads
`localStorage['theme']`. Add the global `img[src*='degruyter-icons']{filter:invert(1)}`
dark rule.
**Done when:** a throwaway page using `var(--c-*)` tokens renders correct in both themes; no
flash of wrong theme on reload.
## Story 2 — Shared: app header + nav (`ArchiveHeader`)
**Spec:** `DESIGN_RULES.md §4`. Prototype: `ArchiveHeader.dc.html`.
- One sticky header component: mint stripe, wordmark, nav with active-key prop, theme
toggle, user chip. Nav routes to all sections.
- Theme toggle drives the Story 1 mechanism.
**Done when:** header renders identically on every route; active item shows the mint
underline; toggle flips theme and persists.
## Story 3 — Shared: avatar + deterministic color
**Spec:** `DESIGN_RULES.md §5`.
- `avatarFor(name)` util (hash → palette index + initials) + an `<Avatar name size>`
component supporting 26/28/40/48px and the overlapping-stack ring variant.
**Done when:** the same name yields the same color everywhere; stacks overlap with the
surface-colored ring.
## Story 4 — Shared: page-header, segmented control, card, metadata, empty state
**Spec:** `DESIGN_RULES.md §3, §6, §7`.
- `PageHeader` (eyebrow + 4px mint left rule + serif h1 + italic lede + right slot).
- `SegmentedControl` (active = navy). `Card` (mint top/left border variants). `MetaLine`
(` · ` separated, optional leading icon). `EmptyState` (dashed, serif + ellipsis).
**Done when:** each primitive matches the prototype and is consumed by the screen stories
below — not re-styled inline.
---
## Story 5 — Dokumente (dashboard / search results)
**Route:** `/`. **Prototype:** `Dokumente.dc.html`.
PageHeader (eyebrow "Archiv", title "Dokumente", lede, right count "147 Dokumente · 38
Personen"). Then: a **search bar card** (input `Titel, Personen, Tags durchsuchen…` +
"Datum ↓" sort + "Filter" buttons); a **resume strip** (mint left border, "Weiter bei:" +
italic underlined doc link); a **`1fr 320px` grid** — left: "Zuletzt hinzugefügt" list
(title · avatar stack · right-aligned date `width:128px` · status dot+label `width:118px`),
right column: **upload dropzone** (dashed, Upload icon, `PDF, JPG, PNG, TIFF bis 50 MB`) +
"Benötigt Metadaten" card; below full-width **Mission Control** — 3 tiles (Segmentierung /
Transkription / Zur Überprüfung), each with a caption, a pill "skill" hint, a weekly count,
and a list of linked items. Right column collapses below `lg`; main goes full-width.
**Done when:** matches prototype both themes; status dots use the §7 colors; grid collapses.
## Story 6 — Personen (directory)
**Route:** `/persons`. **Prototype:** `Personen.dc.html`.
PageHeader (eyebrow "Verzeichnis") + right count "38 Personen". Search input
(`z.B. Oma Frieda, Onkel Karl…`) + segmented control (Alle / Personen / Institutionen /
Gruppen). **3-column card grid**; each card: 48px avatar, serif name, relation sub, optional
type **badge** (Institution/Gruppe/Unbekannt — §1 colors), divider, meta line
`✉ N Briefe · N Dokumente`. Cards carry the 3px mint top border.
**Done when:** badge colors correct; avatar colors deterministic; grid responsive.
## Story 7 — Briefwechsel — DROPPED
The two-person letter-exchange feature was removed from the product. Its prototype, route, and
nav entry no longer exist. Skip.
## Story 8 — Geschichten (story collections list) — NEW
**Route:** `/geschichten`. **Prototype:** `Geschichten.dc.html`.
PageHeader (eyebrow "Sammlungen") + primary button "Neue Geschichte". Segmented control
(Alle / Veröffentlicht / In Arbeit / Entwurf) + Filter button. **2-column card grid**; each
card (link, mint top border): tag chips (dot + UPPERCASE label), serif 24px title, serif dek,
meta line `📅 range · N Dokumente · N Personen`, footer with overlapping avatar stack + status
dot/label. Cards link to the story detail.
**Done when:** tag dots and status colors correct; cards link to Story 9.
## Story 9 — Geschichte (single story detail) — NEW
**Route:** `/geschichten/:id`. **Prototype:** `Geschichte.dc.html`. **Two variants**
(`variant` prop): **"Lesereise"** (a guided reading — intro + narration blocks + letter
cards + annotation notes) and **"Sammlung"** (a collection — intro + "Erwähnte Dokumente"
list). Centered `max-width:880px` article card (mint top border, `padding:48px 56px`):
type badge, 38px serif title, byline row (author avatar + name + "zusammengestellt am …" +
Bearbeiten / Löschen actions), intro paragraph, then ordered **blocks**:
- **narration** — 3px mint left rule, serif italic 18px.
- **letter** — clickable row: 40px tile w/ Mail icon, serif title, meta `date · von X an Y`,
trailing Arrow-Right icon.
- **note** — mint-tinted (`--c-accent-bg`) left-rule box, "Anmerkung" caption + serif italic.
**Done when:** both variants render from the prop; block types styled per spec; Löschen uses
`--c-danger`.
## Story 10 — Zeitstrahl (timeline) — NEW
**Route:** `/zeitstrahl`. **Prototype:** `Zeitstrahl.dc.html`.
PageHeader (eyebrow "Chronik") + right count. Segmented control (Alle / Briefe / Personen /
Ereignisse) + a small legend. **Centered vertical spine** (`max-width:760px`, 2px mint center
line). Item types stacked on the spine: **year** pill (navy), **summary** card (count + a
12-bar monthly-density mini chart in mint + range labels), **letter** cards **alternating
left/right** with a spine dot (`2px solid --c-primary`) and optional tag pill, **person**
node (28px navy circle glyph + name + derived meta), **curated** node (★, mint left rule),
**historical** band (full-width, Globe icon, serif italic, top/bottom hairline).
**Done when:** spine centered; letters alternate; bars scale to value %; all five item types
render.
## Story 11 — Aktivitäten (activity feed) — NEW
**Route:** `/aktivitaeten`. **Prototype:** `Aktivitaeten.dc.html`.
PageHeader (eyebrow "Verlauf") + "Aktualisieren" button. Segmented control (Alle /
Transkription / Uploads / Personen). Feed grouped by day (Heute / Gestern / Diese Woche),
each group a UPPERCASE caption + rows. Each **row**: 40px avatar with a small **action-icon
badge** bottom-right (Check/Upload/Chat/Edit/…), then a sentence — bold actor name +
Montserrat verb + *italic underlined* target link — and a time sub.
**Done when:** grouping + icon badges match; target links styled with mint underline.
## Story 12 (optional) — Regeln (internal style reference)
**Prototype:** `Regeln.dc.html`. Internal page documenting the seven blocks (Typografie,
Farbe, Seitenkopf, Steuerung, Avatare, Metadaten/Leerzustände). Build only if the team wants
a living in-app reference; otherwise `DESIGN_RULES.md` is the canonical record. Gate behind
admin/dev.
---
## Suggested order & dependencies
```
1 Tokens ─┬─ 2 Header ──┐
├─ 3 Avatar ──┼─→ 5 Dokumente, 6 Personen,
└─ 4 Primitives┘ 8 Geschichten → 9 Geschichte,
10 Zeitstrahl, 11 Aktivitäten (parallelizable)
12 Regeln (optional, last)
→ then Phase B (added screens), all depend on 14
```
---
## Phase B — Added screens (previously un-prototyped pages)
These are the pages the original handoff never covered. Each now has a hifi `.dc.html`
prototype in `prototypes/`. All depend on the shared primitives from Stories 14 and are
parallelizable among themselves. **Each maps to one Gitea page-issue** in the milestone.
Admin + OCR pages are explicitly **out of scope** here (phase-2 milestone).
| # | Screen | Prototype | Route(s) | Done when |
|---|---|---|---|---|
| B1 | Dokumente-Liste | `Dokumente-Liste.dc.html` | `/documents` | PageHeader; search card; AND/OR segmented; grouped mint-top cards; avatar-stack rows; 7px status dot+label; pagination |
| B2 | Dokument-Detail | `Dokument-Detail.dc.html` | `/documents/[id]` | compact top bar w/ mint accent bar; PDF pane + transcription panel w/ Lesen/Bearbeiten segmented + turquoise mode; details card |
| B3 | Dokument-Bearbeiten | `Dokument-Bearbeiten.dc.html` | `/documents/[id]/edit`, `/new`, `/bulk-edit` | split pane; progress strip; Wer&Wann + Beschreibung cards; dropzone (new); action bar (Löschen danger / Abbrechen / Zur Überprüfung / Speichern) |
| B4 | PersonDetail | `PersonDetail.dc.html` | `/persons/[id]` | PageHeader; 2-col mint-top cards; deterministic avatar; correspondents + relationships + letter lists |
| B5 | PersonForm | `PersonForm.dc.html` | `/persons/new`, `/[id]/edit` | PageHeader; Stammdaten card w/ type segmented; caps labels; Namensverlauf; edit-only merge danger zone; save bar |
| B6 | PersonReview | `PersonReview.dc.html` | `/persons/review` | PageHeader + count; row card w/ muted avatar; idle/rename/merge states; danger merge+delete; confirm dialog; empty state |
| B7 | Geschichte-Editor | `Geschichte-Editor.dc.html` | `/geschichten/new`, `/[id]/edit` | type-pick segmented; prose editor toolbar; journey editor w/ **color-only** drag; sidebar status+persons; save bar |
| B8 | Ereignis-Editor | `Ereignis-Editor.dc.html` | `/zeitstrahl/events/new`, `/[id]/edit` | PageHeader; Wann&Was card w/ type segmented + date precision + danger error; persons/docs sidebar; save bar |
| B9 | Stammbaum | `Stammbaum.dc.html` | `/stammbaum` | PageHeader + count; node cards w/ **§5 avatar** in resting/selected/dimmed; line connectors; side panel; zoom controls; empty state |
| B10 | Themen | `Themen.dc.html` | `/themen` | PageHeader + count; segmented filter; mint-top cards w/ **§1 tag-dot** (not stripe); child rows; empty state |
| B11 | Anreicherung | `Anreicherung.dc.html` | `/enrich`, `/[id]`, `/done` | list (status rows) / step (progress bar + split pane + action bar) / done (success card) |
| B12 | Profil | `Profil.dc.html` | `/profile`, `/users/[id]` | PageHeader; 2-col data/password cards; token banners; notifications; public profile card w/ avatar |
| B13 | Anmeldung | `Anmeldung.dc.html` | `/login`, `/register`, `/forgot-password`, `/reset-password` | self-contained branded shell (no app header); Tinos sentence-case titles; 17px inputs; token banners |
| B14 | Hilfe-Transkription | `Hilfe-Transkription.dc.html` | `/hilfe/transkription` | PageHeader; article column; rule cards w/ De Gruyter icons (no emoji); fixes `border-brand-sand`/`bg-white` bugs |