diff --git a/docs/specs/stammbaum-doc-badge-spec.html b/docs/specs/stammbaum-doc-badge-spec.html new file mode 100644 index 00000000..14a64d08 --- /dev/null +++ b/docs/specs/stammbaum-doc-badge-spec.html @@ -0,0 +1,987 @@ + + +
+ + ++ Design spec for the inline relationship pill on the Document Detail page. Relationship labels appear + as inline pills directly next to each person's name — both in the 48 px sub-header bar + and in the Personen column of the 3-column metadata drawer. Example: Karl Raddatz + ELTERNTEIL + → Hans Raddatz + KIND. + This is View 2 of 3 in the Stammbaum document-badge feature set. +
+ +All colour values used by the inline pill and its surrounding context. Light and dark themes are shown side by side. Contrast ratios are against the respective surface colour.
+| Pill bg | +rgba(161,220,216,.25) — near-white on white~14:1 AAA ✓ (text on near-white) | +
| Pill border | +#a1dcd8 — mint accent outline | +
| Pill text | +#012851 — navy ink14.5:1 AAA ✓ | +
| Person name | +#4b5563 — Montserrat 11px (sub-header) | +
| Meta person name | +#012851 — Tinos 9.5px (metadata drawer) | +
| Sub-header bg | +#ffffff | +
| Sub-header border | +#e4e2d7 | +
| Arrow (decorative) | +#a1dcd8 — aria-hiddennon-text only |
+
| Meta label | +#6b7280 — Montserrat 9px 700 uppercase4.8:1 AA ✓ | +
| Meta value | +#012851 — Tinos 13px14.5:1 AAA ✓ | +
| Doc title | +Tinos serif 18px · #012851 | +
| Avatar KR | +#012851 — navy | +
| Avatar HR | +#5a2d6f — purple | +
| Pill bg | +rgba(0,199,177,.10) — dark teal washpasses AA ✓ | +
| Pill border | +#00c7b1 — turquoise | +
| Pill text | +#f0efe9 — warm white14.5:1 AAA ✓ | +
| Person name | +#9ca3af — (sub-header) | +
| Meta person name | +#f0efe9 — (metadata drawer) | +
| Sub-header bg | +#011526 | +
| Sub-header border | +#0d3358 | +
| Arrow (decorative) | +#00c7b1 — aria-hiddennon-text only |
+
| Meta label | +#8b97a57.1:1 AAA ✓ | +
| Meta value | +#f0efe914.5:1 AAA ✓ | +
| Doc title | +Tinos serif 18px · #f0efe9 | +
+ ⚠ Pill background rgba(161,220,216,.25) is nearly transparent on white — the effective contrast for the text is calculated against the near-white composite, yielding ~14:1.
+ The arrow between sender and receiver chips in the sub-header is aria-hidden="true" — directional meaning is conveyed by DOM order (sender before receiver) and the visual left-to-right reading order.
+
+ Full document detail page at ~65% scale. Sub-header bar (48 px) shows inline pills next to avatar chips. + Metadata drawer is open, showing pills next to person names in the Personen column. + Both light and dark themes shown side by side. +
+Light. Pills appear in both the sub-header chip row and the metadata Personen column. Arrow between chips is mint-coloured and aria-hidden.
+Dark. Pills flip to rgba(0,199,177,.10) bg, #00c7b1 border, #f0efe9 text. Sub-header and metadata surfaces both use #011526.
++ The 3-column metadata grid collapses to a single stacked column. The sub-header truncates the document + title and moves secondary actions behind a "…" overflow button. Pills remain inline next to person names in both locations. +
+Tablet light. 3-column metadata collapses to single column. Pills stay inline with names. Sub-header shows only title + primary action + overflow menu.
+Tablet dark. Same collapse behaviour. Dark pill tokens apply throughout.
+
+ Sub-header is simplified to back arrow and document title only — no person chips in the bar.
+ Metadata is full-width single column. Each person row is flex; align-items: center; flex-wrap: nowrap
+ — avatar, name, and pill on one line. If the name is very long the row wraps gracefully before the pill.
+ Only primary action buttons are shown.
+
Mobile light. No chips in sub-header — only title + primary action. Person rows: avatar + name + pill, flex-wrap:nowrap. Pill text drops to 6px to fit.
+Mobile dark. Pill tokens #00c7b1/#f0efe9 at reduced 6px font — still passes AA on dark surface.
+Three cases where the pill is silently omitted. The person name renders as normal — no gap, no placeholder.
+inferredRelationship === null because the backend returns 404 (no kinship path). Name renders without trailing pill.inferredRelationship provides.receivers.length > 1 — inference endpoint is never called, inferredRelationship is null. No pill on any person chip.Exact CSS/Tailwind values for every element of the pill and its context. Use these as the ground truth during implementation review.
+| Element | +Tailwind / CSS | +Notes | +
|---|---|---|
| Inline pill (light) | +rounded-full border border-[#a1dcd8] bg-[rgba(161,220,216,.25)] px-2 py-0.5 text-[9px] font-bold uppercase tracking-[.07em] text-[#012851] ml-2 align-middle inline |
+ Montserrat 9px 700. ml-2 = 8px from name span. vertical-align: middle aligns cap-height to person name. |
+
| Inline pill (dark) | +dark:bg-[rgba(0,199,177,.10)] dark:border-[#00c7b1] dark:text-[#f0efe9] |
+ All three dark overrides applied together. Rest of pill class unchanged. | +
| Person name span | +font-sans text-[11px] text-[#4b5563] dark:text-[#9ca3af] (sub-header) or font-serif text-[9.5px] text-ink dark:text-[#f0efe9] (metadata) |
+ Name and pill share a flex items-center gap-0 wrapper. Pill is the immediate next sibling of the name <span>. |
+
| Sub-header chip area | +flex items-center gap-1.5 |
+ Wraps one sender chip + arrow + one receiver chip. Placed after the doc-title block, before action buttons. | +
| Chip (avatar + name + pill) | +flex items-center gap-1 |
+ Avatar, name span, and pill as three siblings inside the chip div. | +
| Arrow between chips (sub-header) | +h-2.5 w-2.5 shrink-0 text-[#a1dcd8] dark:text-[#00c7b1] with aria-hidden="true" |
+ Arrow SVG carries no semantic information. DOM order (sender chip before receiver chip) conveys direction for assistive technology. | +
| Person avatar (sub-header) | +w-5 h-5 rounded-full flex items-center justify-center text-[6px] font-bold text-white shrink-0 |
+ 20×20 px. Initials in 6px bold white. Background colour is person-specific (set inline). | +
| Person avatar (metadata) | +w-5 h-5 rounded-full flex items-center justify-center text-[6.5px] font-extrabold text-white shrink-0 |
+ Same 20×20 px. Slightly heavier weight (800) to match existing drawer card style. | +
| Pill condition | +{#if inferredRelationship} … {/if} wraps both the sender pill and the receiver pill |
+ Render only when inferredRelationship !== null && receivers.length === 1. The check lives in +page.server.ts, not in the component. |
+
| Pill label value | +inferredRelationship.labelFromA next to sender, inferredRelationship.labelFromB next to receiver |
+ Labels are pre-translated strings from the backend. No frontend i18n key needed for the label text itself. | +
| Mobile person row | +flex items-center gap-1 flex-nowrap |
+ flex-wrap: nowrap keeps avatar + name + pill on one line. If name overflows container, truncate name with truncate, never truncate the pill. |
+
| Mobile pill font-size | +text-[6px] at ≤375 px |
+ Reduced from 9px (desktop) to 6px on mobile to fit without overflow. Contrast still passes AA at 6px bold. | +
| Sub-header at mobile | +Chips removed entirely from sub-header at max-width: 767px |
+ Sub-header shows only back arrow + document title + primary action button. Person chips with pills appear only in the metadata section on mobile. | +
+ Accessibility note: The pill text ("ELTERNTEIL", "KIND") is uppercase visually but the accessible name should be the mixed-case label from the backend (labelFromA). Apply aria-label={labelFromA} on the pill span so screen readers announce "Elternteil" not "E-L-T-E-R-N-T-E-I-L". The visual uppercase is achieved with CSS text-transform: uppercase, not by changing the source string.
+
+ Spec for the new Stammbaum & Beziehungen card appended at the bottom of
+ /persons/{id}/edit. The card lets editors toggle a person's family-tree membership
+ and manage their direct relationships (parents-of, spouse, siblings, etc.). Inferred transitive
+ relationships are shown in a collapsible derived section.
+
All colour values used by the new card. Components use Tailwind semantic tokens; exact hex values are provided here for reference only.
+| Page bg | +#f0efe9 — sand | +
| Card bg / border | +#ffffff / 1px #e4e2d7 | +
| Section label | +#6b7280 — Montserrat 9px 700 uppercase4.8:1 AA ✓ | +
| In-tree indicator bg | +rgba(161,220,216,.1) bg / 1px #a1dcd8 border | +
| In-tree label | +#01285114.5:1 AAA ✓ | +
| Toggle ON track | +#012851 | +
| Toggle OFF track | +#e4e2d7 | +
| Toggle thumb | +#ffffff | +
| Direct rel pill bg/border | +rgba(161,220,216,.2) / 1px #a1dcd8 | +
| Direct rel name | +#012851 — Tinos 10px | +
| Rel years | +#6b7280 — Montserrat 8px | +
| Delete btn / hover | +#6b7280 normal / #c0392b hover | +
| Add-form bg / border | +#f5f4ef / #e4e2d7 | +
| Derived pill bg/border | +#f5f4ef / #e4e2d7 | +
| + Hinzufügen btn | +#012851 bg / #fff text | +
| Page bg | +#010e1e | +
| Card bg / border | +#011526 / 1px #0d3358 | +
| Section label | +#8b97a57.1:1 AAA ✓ | +
| In-tree indicator bg | +rgba(0,199,177,.08) bg / 1px #00c7b1 border | +
| In-tree label | +#00c7b1 | +
| Toggle ON track | +#a1dcd8 (mint) | +
| Toggle thumb (ON) | +#012851 | +
| Direct rel pill bg/border | +rgba(0,199,177,.12) / 1px #00c7b1 | +
| Direct rel name | +#f0efe9 | +
| Rel years | +#8b97a5 | +
| Add-form bg / border | +#011a30 / #0d3358 | +
| Derived pill bg/border | +#011a30 / #0d3358 / text #9ca3af | +
| + Hinzufügen btn | +#a1dcd8 bg / #012851 text | +
+ Full person-edit page at desktop width. Light mockup shows the + add-form expanded; dark mockup shows the resting state + (form collapsed, toggle ON, no derived section open). Shown at ~40% scale. +
+Desktop, light. Toggle is ON → in-tree indicator visible. "Beziehung hinzufügen" form is expanded, showing Typ dropdown, Person typeahead, Von/Bis year inputs, and "+ Hinzufügen" button. Derived section is collapsed with count badge (5).
+Desktop, dark. Resting state — add form hidden, "+ Beziehung hinzufügen" button visible. Toggle track is mint (#a1dcd8), thumb is navy (#012851). In-tree indicator border shifts to turquoise #00c7b1.
++ Narrower viewport — card fills the single center column, paddings tighten. Same component + structure as desktop. Shown at ~50% scale. +
+Tablet. Card fills single column. Two relationship rows shown (trimmed for space). All controls remain the same size — no stacking yet at 768 px.
++ Phone viewport. Card is full-width. Add-form fields stack vertically (single column). + Pill labels truncate to first word on very small screens if needed. Shown at ~65% scale. +
+Mobile. "Typ" and "Person" fields are stacked vertically. Year inputs remain side-by-side. Pill labels shrink to 6 px. Card fills the full width with 7 px side padding.
++ Clicking the "Abgeleitete Beziehungen" row expands the section. Items are computed + server-side from the direct-relationship graph — the frontend renders them read-only. + Grey pills and muted italic text for entries that cannot be resolved. +
+Derived section expanded. Chevron rotates to ▴. Items use grey pills and Tinos italic for unresolvable entries. Section is read-only — no edit or delete controls. Computed by the backend graph traversal.
+| Element | +Tailwind / CSS | +Notes | +
|---|---|---|
| Card wrapper | +bg-white border border-line rounded-sm p-6 |
+ Matches existing Angaben and Namensverlauf cards on same page | +
| Section label | +text-xs font-bold uppercase tracking-widest text-gray-400 mb-4 |
+ Montserrat 9px 700; identical to all other cards on the page | +
| Card header row | +flex items-center justify-between mb-3 |
+ Section label on left; toggle label + toggle on right | +
| Toggle track ON | +w-[38px] h-5 rounded-full bg-primary transition-colors |
+ dark: bg-accent (#a1dcd8) |
+
| Toggle track OFF | +w-[38px] h-5 rounded-full bg-line |
+ bg-line = #e4e2d7 |
+
| Toggle thumb | +absolute top-0.5 w-4 h-4 rounded-full bg-white transition-all |
+ ON: left-[19px]; OFF: left-0.5; dark ON thumb: bg-primary |
+
| In-tree banner | +flex items-center justify-between px-2.5 py-1.5 bg-accent/10 border border-accent rounded-sm mb-3 |
+ Hidden when toggle is OFF. dark: bg-[rgba(0,199,177,.08)] border-[#00c7b1] |
+
| In-tree dot | +w-[7px] h-[7px] rounded-full bg-primary shrink-0 |
+ dark: bg-[#00c7b1] |
+
| In-tree label | +text-[9px] font-bold uppercase tracking-widest text-primary |
+ dark: text-[#00c7b1] |
+
| In-tree link | +text-[9px] font-semibold text-primary/60 |
+ Navigates to /stammbaum?focus={personId} |
+
| Rel row wrapper | +flex items-center gap-2 py-1.5 border-b border-muted last:border-b-0 |
+ Padding 7px top/bottom; bottom border except last row | +
| Direct rel pill | +rounded-full border border-accent bg-accent/20 px-2 py-0.5 text-[9px] font-bold uppercase tracking-wider text-ink shrink-0 |
+ dark: bg-[rgba(0,199,177,.12)] border-[#00c7b1] text-sand |
+
| Rel person name | +font-serif text-[13px] text-ink flex-1 min-w-0 truncate |
+ Tinos; truncate prevents overflow on narrow viewports | +
| Rel year range | +font-sans text-[10px] text-ink-3 whitespace-nowrap |
+ Format: ab {year} or {from}–{to} |
+
| Delete button | +text-ink-3 hover:text-danger p-0.5 ml-auto shrink-0 |
+ ✕ icon button; hover color #c0392b; min touch 44px via padding wrapper |
+
| Add-rel quiet button | +flex items-center gap-1 text-[8px] font-bold text-primary/60 hover:text-primary/90 mt-1 |
+ + icon (heroicons plus-mini); hidden when add form is open | +
| Add-form card | +mt-3 bg-muted border border-line rounded-sm p-3.5 |
+ bg-muted = #f5f4ef; dark: bg-[#011a30] border-[#0d3358] |
+
| Type select | +w-full border border-line rounded-sm px-2.5 py-2 text-xs font-sans text-ink bg-white |
+ Options: Elternteil von / Ehegatte / Geschwister von / Kollege / Freund / Arbeitgeber / Arzt / Nachbar / Sonstiges | +
| Person input | +w-full border border-line rounded-sm px-2.5 py-2 font-serif text-sm text-ink bg-white |
+ Drives PersonTypeahead component; placeholder "z.B. Oma Frieda…" |
+
| Year inputs | +flex-1 border border-line rounded-sm px-2.5 py-2 font-serif text-sm text-ink bg-white |
+ Side-by-side via flex gap-2; on mobile remain side-by-side (they're short) |
+
| + Hinzufügen button | +bg-primary text-white px-3.5 py-2 text-[10px] font-bold uppercase tracking-wider rounded-sm shrink-0 |
+ dark: bg-accent text-primary |
+
| Cancel button | +bg-white border border-line rounded-sm px-3 py-1.5 text-[10px] font-bold text-ink-2 |
+ Collapses add form without saving | +
| Derived section header | +flex items-center justify-between py-2 border-t border-muted cursor-pointer mt-2.5 select-none |
+ Click toggles derivedExpanded boolean in component state |
+
| Derived count badge | +ml-1.5 rounded-full border border-line bg-muted px-1.5 py-0 text-[7px] font-bold text-ink-3 |
+ Shows total inferred relationship count from backend | +
| Derived rel pill | +rounded-full border border-line bg-muted px-2 py-0.5 text-[9px] font-bold uppercase tracking-wider text-ink-2 shrink-0 |
+ Grey, no accent border; read-only only | +
| Derived rel name | +font-serif text-[13px] text-ink-3 |
+ Muted; unresolvable entries use italic text-ink-4 |
+
| Mobile form stacking | +sm:flex-row flex-col on add-form top row |
+ Below 640 px Typ and Person stack; year inputs stay side-by-side | +
+ Visual specification for the generational family tree view. Covers the SVG canvas with + Gen I–III nodes, the 268 px side panel (resting and add-form states), and responsive + behavior at 768 px (tablet, slide-over) and 375 px (mobile, horizontal scroll). + Light and dark themes throughout. +
+ +All colour values used by the tree canvas, nodes, connectors, and side panel. The component uses only semantic Tailwind tokens — no hardcoded hex. Light and dark themes are handled automatically by layout.css.
| Canvas bg | +#f0efe9 — sand page background | +
| Node bg default | +#ffffff — white | +
| Node bg selected | +#012851 — navyAAA on white acc. bar | +
| Node accent bar | +#a1dcd8 — mint, 4 px left strip on selected | +
| Node border default | +#012851 1.5 px | +
| Connector / dot | +#012851 stroke-width 1.5, r=4.5 | +
| Name text default | +#01285114.5:1 AAA ✓ | +
| Name text selected | +#ffffff on #01285114.5:1 AAA ✓ | +
| Years text default | +#6b72804.8:1 AA ✓ | +
| Years text selected | +rgba(255,255,255,.6) — decorative, aria-hidden years | +
| Gen label | +#6b7280 — 8 px, tracking 2 px, aria-hidden | +
| Panel surface | +#ffffff | +
| Direct pill | +rgba(161,220,216,.2) bg · #a1dcd8 border · #012851 text | +
| Derived pill | +#f5f4ef bg · #e4e2d7 border · #4b5563 text | +
| Canvas bg | +#010e1e | +
| Node bg default | +#011526 | +
| Node bg selected | +#a1dcd8 — mint inverted | +
| Node accent bar | +#012851 — navy inverted | +
| Node border default | +#1e3a55 1.5 px | +
| Connector / dot | +#3a6080 stroke-width 1.5 | +
| Name text default | +#f0efe914.5:1 AAA ✓ | +
| Name text selected | +#012851 on #a1dcd89.2:1 AAA ✓ | +
| Years text default | +#8b97a57.1:1 AAA ✓ | +
| Gen label | +#4e6070 — aria-hidden | +
| Panel surface | +#011526 | +
| Direct pill | +rgba(0,199,177,.12) bg · #00c7b1 border · #f0efe9 text | +
| Derived pill | +#011a30 bg · #0d3358 border · #9ca3af text | +
Full tree canvas with Gen I–III nodes and the 268 px side panel showing Maria Raddatz (selected). Rendered at ~65 % scale. Light and dark stacked.
+Light. Maria Raddatz (Gen I, right) is selected — navy fill, mint accent bar, white text. Side panel at right shows direkte Beziehungen as mint pills and abgeleitete Beziehungen as sand pills. "+" add button is quiet / low-opacity.
+Dark mode. Maria's node inverts to mint (#a1dcd8) fill with navy accent bar and navy text — the exact inverse of light. Direct pills use #00c7b1 border on near-black bg; derived pills use #0d3358 border. Canvas bg is #010e1e.
+Left: resting state — "+" text button is quiet (low opacity) below the direct relationships list. Right: add-form expanded — a compact inline form replaces the quiet button. No modal.
+Resting. "+ Beziehung hinzufügen" sits below direct-relationship pills at 40% opacity. It is a button, not a link. Derived relationships are always read-only (no add button).
+Expanded. Clicking "+" reveals a compact inline form: relationship-type select (full width), focused person-search input (navy border = focus indicator), optional Von/Bis Jahr row, and Abbrechen + Speichern. No modal, no overlay.
+Tree fills the full viewport width. The 268 px side panel is hidden by default and slides up as a bottom sheet when the user taps a node. Mode toggle and zoom controls remain visible in the page header.
+Tablet. The 268 px side panel column is removed; the tree card spans full page width (minus 32 px gutter). Tapping any node slides up a bottom sheet containing the same panel content. Mode toggle and zoom buttons remain in the top bar.
+App header is abbreviated (logo + avatar only, no nav links). "Stammbaum" appears as a page-level h1 below the header. The tree card overflows horizontally — the user scrolls left/right. Zoom controls are removed on mobile.
+Mobile 375 px. Header is abbreviated — logo + avatar, no nav links. "Stammbaum" is a standalone h1. Baum/Liste mode toggle is full width. Tree card has overflow-x: scroll; the SVG renders at full 820 px width and the user pans. Zoom controls removed.
| Element | +Tailwind / CSS | +px value | +Notes | +
|---|---|---|---|
| Tree node | +w-[144px] h-[50px] rounded-sm |
+ 144 × 50 | +SVG rect rx="2"; same size all generations |
+
| Selected accent bar | +w-1 h-full bg-accent |
+ 4 × 50 | +Mint on light; navy on dark; left edge of selected node | +
| Side panel | +w-[268px] shrink-0 |
+ 268 | +Hidden at ≤ 768 px; becomes bottom sheet on node tap | +
| Tree + panel gap | +gap-[18px] |
+ 18 | +Flex row gap between tree card and side panel | +
| Page padding | +px-8 py-6 |
+ 32 / 24 | +Reduced to px-3 py-3 on mobile |
+
| Direct pill | +rounded-full px-2 py-0.5 text-[9px] |
+ — | +Mint border rgba(161,220,216,.2) bg; #a1dcd8 border light | +
| Derived pill | +rounded-full px-2 py-0.5 text-[9px] |
+ — | +#f5f4ef bg; #e4e2d7 border light; read-only, no add button | +
| Connector stroke | +SVG stroke-width="1.5" |
+ 1.5 px | +#012851 light · #3a6080 dark | +
| Marriage dot | +SVG r="4.5" |
+ 9 px dia | +Filled circle at connector midpoints; same color as connectors | +
| Gen label | +text-[8px] tracking-[2px] uppercase |
+ 8 px | +aria-hidden="true"; #6b7280 light · #4e6070 dark |
+
| Node name text | +font-serif text-[10px] font-bold |
+ 10 px | +Two lines: given name y+20, surname y+32; text-anchor middle at cx | +
| Node years text | +font-sans text-[8px] |
+ 8 px | +y+44 from node top; subdued color; not aria-hidden (content) | +
| Zoom controls | +flex border rounded-sm overflow-hidden |
+ 26 × 26 each | +Hidden on mobile (≤ 640 px) | +
| Mode toggle (mobile) | +flex w-full border rounded-sm overflow-hidden |
+ full width | +Two segments: Baum (active) / Liste; full width on mobile only | +
| Tree card scroll | +overflow-x-auto |
+ — | +Applied to tree card wrapper on mobile; SVG renders at full 820 px | +