# Stammbaum — Tree Page · /stammbaum

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.

Familienarchiv · 2026-04-27 · Leonie Voss, UX Lead
Stammbaum Feature View 1 of 3 — Tree Page Desktop / Tablet / Mobile Light + Dark

## 1 · Design tokens

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`.

Light theme

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
Gutter label | #4b5563 (var(--c-ink-2)) — 12 px, tracking 0.08 em, font-weight 700, screen-reader-labelled
Gutter stripe | rgba(161,220,216,.08) — decorative full-row underlay alternating with transparent
Panel surface | #ffffff
Direct pill | rgba(161,220,216,.2) bg · #a1dcd8 border · #012851 text
Derived pill | #f5f4ef bg · #e4e2d7 border · #4b5563 text

Dark theme

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 ✓
Gutter label | #9ca3af (var(--c-ink-2)) — 12 px, tracking 0.08 em, font-weight 700, screen-reader-labelled
Gutter stripe | rgba(161,220,216,.14) — decorative full-row underlay alternating with transparent
Panel surface | #011526
Direct pill | rgba(0,199,177,.12) bg · #00c7b1 border · #f0efe9 text
Derived pill | #011a30 bg · #0d3358 border · #9ca3af text

## 2 · Desktop (1280 px) — light & dark, resting state

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.

Mockup `<rect>` dimensions (144 × 50) below are illustrative; authoritative node geometry is in the impl-ref constants table in §6 (`NODE_W = 160`, `NODE_H = 56`).

Light theme
Stammbaum
−
+
MR
Maria Raddatz
1855–1932
Direkte Beziehungen
EhegatteHeinrich Raddatz ElternteilKarl Raddatz ElternteilErnst Raddatz
+ Beziehung hinzufügen
Abgeleitete Beziehungen
GroßelternteilHans Raddatz GroßelternteilLotte Berger GroßelternteilWerner Raddatz

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 theme
Stammbaum
−
+
MR
Maria Raddatz
1855–1932
Direkte Beziehungen
EhegatteHeinrich Raddatz ElternteilKarl Raddatz ElternteilErnst Raddatz
+ Beziehung hinzufügen
Abgeleitete Beziehungen
GroßelternteilHans Raddatz GroßelternteilLotte Berger GroßelternteilWerner Raddatz

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.

## 3 · Side panel states — light

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 — quiet add button
MR
Maria Raddatz
1855–1932
Direkte Beziehungen
EhegatteHeinrich Raddatz ElternteilKarl Raddatz ElternteilErnst Raddatz
+ Beziehung hinzufügen
Abgeleitete Beziehungen
GroßelternteilHans Raddatz GroßelternteilLotte Berger GroßelternteilWerner Raddatz

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).

Add-form expanded
MR
Maria Raddatz
1855–1932
Direkte Beziehungen
EhegatteHeinrich Raddatz ElternteilKarl Raddatz ElternteilErnst Raddatz
Abbrechen
Speichern

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.

## 4 · Tablet (768 px) — light

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.

768 px · no persistent side panel
Stammbaum
−
+
Side panel appears as bottom sheet on node tap

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.

## 5 · Mobile (375 px) — light

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.

375 px · horizontal scroll tree
Familienarchiv
M

# Stammbaum

Baum
Liste

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.

## 6 · Implementation reference

Element | Tailwind / CSS | px value | Notes
--- | --- | --- | ---
Tree node | `w-[160px] h-[56px] rounded-sm` | 160 × 56 | SVG `rect rx="2"`; same size all generations. Constants `NODE_W = 160`, `NODE_H = 56` in `buildLayout.ts` are the source of truth.
Selected accent bar | `w-1 h-full bg-accent` | 4 × 56 | 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="6"` | 12 px dia | Filled circle at connector midpoints; same color as connectors. Informational (disambiguates stacked marriages on multi-spouse rows) — WCAG 1.4.11 (3:1) applies.
Gutter label | `font-sans text-[12px] font-bold tracking-[0.08em] uppercase` | 12 px | Wrapped in `<g role="text" aria-label="Generation N">` so screen readers announce the full word. Fill `var(--c-ink-2)`. Renders the un-shifted `node.generation` value, never the post-normalise rank.
Gutter stripe underlay | SVG `<rect>` aria-hidden, alternating `transparent` / `var(--c-gutter-stripe)` | NODE_H + ROW_GAP tall, full viewBox width | Decorative band per occupied generation row. Hidden entirely below `md` (GUTTER_WIDTH = 0).
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

## 6.1 · Layout rules

Invariants honoured by `buildLayout.ts` and asserted in `buildLayout.test.ts`:

- Seeded-rank invariant. When `node.generation` is non-null, the node renders at row index equal to `node.generation` (modulo the negative-generation shift that normalises `min(rank) → 0`). This is independent of edge structure: spouse-pulldown and parent-fallback never move a seeded rank.