Highlight the selected person's bloodline on the family tree #703
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
On the family tree (Stammbaum) page, clicking a person opens a side panel. Today the rest of the tree stays at full visual weight, so a reader cannot easily trace a single line of heritage — the whole tree competes for attention.
This adds a focus + dim (lineage highlighting) layer tied to the existing side panel: while a person is selected, their bloodline stays at full strength and everyone else is dimmed.
This is a read-path enhancement. Readers skew toward phones, so the touch path matters.
User story
Priority: Should · Kano: Delighter
Scope decisions (resolved)
SIBLING_OFedges never pull a person into the active set.StammbaumNodeselected-node styling (active fill + mint accent bar). That accent bar is the cue that marks which person anchors the highlight within an all-active lineage. No additional emphasis.?focus={id}— the tree must render already dimmed, with no click event.--c-primary; dimmed people and connectors render the same tokens at reduced opacity (≈0.4 /opacity-40). This is theme-correct in both light and dark mode (the tokens are theme-aware:--c-primaryis navy in light mode, mint in dark mode) and provides a non-colour lightness cue.Acceptance criteria
?focus={id}(panel open from the start), when the page loads, then the tree renders already dimmed for that person's lineage — no click required.PARENT_OFchain, when the lineage is computed, then the walk terminates (no hang) and the cycle members resolve to a defined active/dimmed state.System rules (EARS)
Non-functional requirements
Implementation notes (from review)
frontend/src/lib/person/genealogy/highlightLineage.tsbesidebuildLayout.ts, depending only onRelationshipDTO[]+ a root id. It returns the active idSet<string>and the connector predicate.StammbaumNode/StammbaumConnectorsstay presentation-only and receive plainactive/dimmedprops — no traversal logic in components.highlightLineage.test.tsfirst, fixtures modelled onbuildLayout.test.ts. Cover: isolated person, ancestors-only, descendants-only, pedigree fan-out (both parents + all four grandparents), multiple spouses/remarriage, spouse boundary (in-law active, in-law's parents dimmed),SIBLING_OFexcluded, connector both-endpoints rule (incl. spouse→in-law-parent dimmed), cyclic-data termination, and initial-load-with-?focus.$derived.by(...)fromselectedId+data.edges; never$effect. Build the adjacency index (parent→children, child→parents, spouse pairs) once in a$derivedkeyed ondata.edges.prefers-reduced-motion(reuse the existinganimateViewpattern).vitest-browser-svelte): assert active vs dimmed rendering via class/attribute, not screenshots; verify re-select recomputes (AC6) and close clears (AC7). E2E not required (browser tests are CI-only).docs/GLOSSARY.mdentry for "lineage highlight". No ADR, no PUML, nogenerate:api(view-only, no backend change).Out of scope
/api/networkalready delivers the full family graph client-side.👨💻 Felix Brandt — Senior Fullstack Developer (re-review)
The implementation notes now capture everything I'd have asked for — pure
highlightLineage.tsbesidebuildLayout.ts,$derivedover$effect, visited-set guard, presentation-only components, TDD matrix. Clear to build.One concrete note
<g opacity="0.4">so the rect, stroke, and text fade together as one unit. Per-attribute opacity on each<rect>/<text>is more code and risks the label and box drifting out of sync. The active set then just decides which group an element lands in.selectedId(the raw rune), not theselectedNodederived —selectedNodeis a.find()lookup and the walk only needs the id.Open Decisions (none)
🏛️ Markus Keller — Application Architect (re-review)
Addressed. The body now states it plainly: no backend/endpoint/schema change, one pure module at the same boundary as
buildLayout, adjacency index built once, and the only doc touch is a GLOSSARY line. Altitude and boundaries are correct.No concerns and no open decisions from my angle.
🔧 Tobias Wendt — DevOps & Platform Engineer (re-review)
No concerns. Still a client-side-only change — no infrastructure, config, secret, or CI surface. Nothing for me to operate.
🛡️ Nora Steiner ("NullX") — Application Security Engineer (re-review)
My one finding is now codified: REQ-STAMMBAUM-04 + AC10 mandate the visited-set guard against cyclic
PARENT_OFdata (CWE-835), with a test for it. No new attack surface — client-side transform over already-authorized data, no input or DOM sink.No concerns and no open decisions.
🧪 Sara Holt — QA Engineer & Test Strategist (re-review)
The test matrix, the
?focusinitial-load case (AC9), and the cyclic-termination case (AC10) are all in the body now, at the right layer. Good.One tightening
Open Decisions (none)
🎨 Leonie Voss — UI/UX & Accessibility Lead (re-review)
The opacity-dim decision, kept accent bar, reduced-motion gating, and the mobile-centring behaviour (AC8) are all captured correctly, and NFR-A11Y-001 now reflects the lightness-cue rationale. This is theme-safe and accessible.
One implementation guard
bg-surfacein both light and dark mode (dark mode dims mint, which is already light). If 0.4 reads too faint in either theme, 0.45–0.5 is fine; the requirement is "clearly de-emphasised but still legible," not invisible. Confirm visually at 320px during build.Open Decisions (none)
👨💻 Implemented — PR #704
Built on
feat/issue-703-stammbaum-lineage-highlight(off latestmain), TDD throughout. PR: #704.Commits
7a655ce6feat(stammbaum): add lineage highlight traversal module — purehighlightLineage.tsbesidebuildLayout.tsf6da9501feat(stammbaum): dim a node when outside the highlighted lineage —StammbaumNodedimmedprop, group-level opacity9f5d7b85feat(stammbaum): dim connectors outside the highlighted lineage —isConnectorActivepredicate, per-connector<g opacity>a3858b6cfeat(stammbaum): bind the lineage highlight to the selected person —StammbaumTree$derivedwiring + AC1/2/6/7 tests0a7b4fa2feat(stammbaum): add recentreAbove pan helper for the mobile anchor4583ee2cfeat(stammbaum): centre the tapped person above the bottom sheet — mobile auto-centre (AC8)e5784caadocs(glossary): define "lineage highlight"Decisions taken (clarified with the maintainer)
frontend/src/lib/person/genealogy/layout/highlightLineage.ts— truly besidebuildLayout.ts(the issue's literal path predated thelayout/reorg).recentreAbovehelper), rather than a plain geometric centre.Acceptance criteria
highlightLineage.test.tsfor the traversal incl. cyclic-termination exact-membership;StammbaumTree.svelte.test.tsfor AC1/2/6/7; AC9 falls out of theselectedId-seeded$derived).recentreAbovegeometry unit-tested + mobile wiring; the touch interaction itself verifies in CI / manually.Notes for review
*.svelte.test.ts) run in CI per project convention.DIMMED_OPACITY = 0.4reads too faint in either theme, bump to 0.45–0.5 — it's a single constant inhighlightLineage.ts.npm run build✅ · node-project tests ✅ (57 passed) ·npm run check— no new errors in touched files.Next:
/review-pron #704.