Architecture Decision Record · 0046

node.person Content-Type + card-person SDC (prop-driven Sibling) + CardPerson Canvas-Code-Component

TL;DR — Generischer node.person-Content-Type mit View-Mode card_quote, der ueber eine neue prop-driven SDC byte_theme:card-person rendert. Die SDC ist der 4. Sibling der Card-Familie und folgt dem in ADR-0009 etablierten „prop-driven fuer View-Mode-Listen-Render"-Pattern. Editor-Platzierungs-Anker ist eine Canvas-Code-Component CardPerson.jsx, die einen personNid-Prop akzeptiert und die Person via JSON:API laedt. Im MVP eine simple Default-Person via config_pages.career_settings.field_default_recruiter; die granulare Vacancy↔Person-Verknuepfung (Personio-Mapping, ER-Feld pro Vacancy, Verhandlung von ADR-0026) ist explizit deferred zu einer Folge-Story.

Kontext

Die Karriere-Stellenanzeigen-Detailseite zeigt heute einen Ansprechpartner-/HR-Kontakt-Block (Live-Beispiel: Laura Gerardi, HR Manager, Tel. 07121/1277-344). Der Block ist Teil des Legacy-Karriere-Systems, kein Drupal-Content-Type. Story P2503-97 („Personen / Team Content Type", Backlog-Position F-170, Parent-Epic P2503-89 „Module & Konfigurationen") fordert einen generischen node.person-Content-Type, der den HR-Block ersetzt und langfristig weitere Use-Cases (Team-Seite, About-Page, Blog-Autoren) traegt.

Vor der Refinement-Session lagen sechs relevante Quellen vor:

  • Jira-Story P2503-97 mit User-Story-Text, ohne Akzeptanzkriterien.
  • Figma-Frame 62110:27008 aus SOLCOM — Design System (ContentItem-Komponente, Variants person / title).
  • Live-Screenshot der HR-Card auf karriere.solcom.de/de/jetzt-bewerben/offene-stellen/….
  • Story-Hub docs/stories/F-170-personen-team-content-type.html im Status IDEE.
  • ADR-0014, der Recruiter-interne Strings im Personio-field_body als „bekannte Restbedingung" erwaehnt.
  • Plan 2026-05-15-001-feat-personio-rest-import.html, dessen Slice-3-AK explizit verlangt: „Description-Aggregation excludes ‚Team' und ‚Ansprechpartner/in'-Sections".

Beobachtete Spannungen

Drei wesentliche Spannungen zwischen den Quellen:

  • Scope-Konflikt zwischen dem generischen Story-Titel („Personen / Team Content Type") und dem konkreten HR-Recruiter-Kontext (Figma-Frame + Live-URL). Der User hat sich im Grilling fuer den generischen Content-Type entschieden — der Figma-Frame ist eine View-Mode-Anwendung, nicht der Scope der Story.
  • Visual-Diff Figma vs. Live: Figma-Frame zeigt horizontal (Foto links, Position klein + Vorname Name gross rechts), Live zeigt vertikal (Foto oben, Name + Funktion + Telefon unten). Die Layouts haben kaum strukturelle Schnittmenge. User-Entscheidung: Live wird ersetzt, Figma-Layout ist Target — aber die Kontakt-Optionen aus Live (Tel, Email, LinkedIn) werden als Stack unter Name ERGAENZT. Das Resultat ist ein „Figma + Extension"-Hybrid, der im solcom-figma-reviewer-Diff dokumentiert sein muss, sonst wird es als Drift gemeldet.
  • Personio-Filter: Plan 2026-05-15-001 entfernt die Ansprechpartner/in-Sections aktiv aus dem importierten field_body. Es gibt heute kein strukturiertes Recruiter-Feld auf node.vacancy. ADR-0026 schliesst zusaetzlich den Editorial-Workflow fuer Vacancy aus (Personio = Single-Source-of-Truth). Daraus folgt eine Luecke: wer/wie verknuepft eine Vacancy mit der richtigen Person?

Card-Familie heute

Nach ADR-0009 und ADR-0038 stehen im byte_theme heute folgende Card-relevante SDCs (Stand 2026-05-25):

SDCPatternRender-QuelleVisual-FrameStatus
bubble-card-frameSVG-LayerInclude-onlyNotch- / no-Notch-Variantphysisch vorhanden
card-frameSlotCanvas-Page-BuilderRechteck (ADR-0038)physisch vorhanden
cardProp-drivenView-Mode-Liste (Blog)Bubble ohne Notch (gradient outline)physisch vorhanden
card-featureSlotCanvas-Page-BuilderFeature-Blockphysisch vorhanden
card-meta-listSlotCanvas-Page-Builder (Vacancy)Meta-Liste (ADR-0013)physisch vorhanden
card-bubble-profileSlotCanvas-Page-BuilderBubble mit Notch (CTA)geplant (MAPPING, noch nicht gebaut)
card-bubble-profile-no-buttonSlotCanvas-Page-BuilderBubble ohne Notchgeplant (MAPPING, noch nicht gebaut)

Hinweis: card-bubble-profile und card-bubble-profile-no-button sind in docs/design/MAPPING.html als geplante Sibling-SDCs eingetragen, existieren jedoch noch nicht im Dateisystem unter web/themes/custom/byte_theme/components/ (Verifikation Stand 2026-05-25). Die Counts und Argumente in dieser ADR beziehen sich daher auf physisch vorhandene SDCs.

Das prop-driven card ist Blog-spezifisch (Felder heading_text, text, media). Eine Erweiterung um Person-Felder (name, funktion, phone, …) wuerde Conditional-Schema einfuehren, das in ADR-0004 bereits als Anti-Pattern verworfen wurde („dedizierte SDCs schlagen Mode-Switch").

Entscheidung

Wir bauen drei neue Artefakte als kohaerentes Paket fuer den View-Mode-Listen-Render von Personen:

1. Content-Type node.type.person

Generischer Personen-Content-Type, NICHT vacancy-spezifisch. Sechs Felder:

node.type.person
  bundle: person
  fields:
    field_name           string,    required
    field_funktion       string,    required
    field_photo          image,     required
    field_phone          telephone, required (klickbar als tel:-Link)
    field_email          email,     optional (klickbar als mailto:-Link)
    field_linkedin_url   link,      optional (klickbar als externer Link)

2. View-Mode card_quote + SDC card-person

entity.entity_view_display.node.person.card_quote rendert node.person ueber die neue SDC byte_theme:card-person. Die SDC ist:

  • prop-driven (kein Slot, kein Canvas-Wrapper-Pattern) — folgt ADR-0009 fuer View-Mode-Listen-Render.
  • kein bubble-card-frame-Include: Figma-Frame 62110:27008 hat keinen Notch und keine Outline. Die Card lebt visuell ohne SOLCOM-Bubble-Layer.
  • nur person-Variant: Der Figma-Frame hat zwei Boolean-Variants (person und title). Die SDC card-person rendert ausschliesslich den person-Variant. Der title-Variant (Editorial-Quote-Block) ist explizit out-of-scope und bekommt eigene SDC, falls priorisiert.
  • Layout-Extension dokumentiert: Figma zeigt nur Position + Vorname Name. Die SDC ergaenzt einen Kontakt-Stack (Tel + Email + LinkedIn) unter Name. Abweichung wird in der Story-Hub-Section <section id="figma-extensions"> festgehalten, damit solcom-figma-reviewer die Extension als „intentional" abhaken kann.

Schema-Skizze:

name: "Card · Person"
group: Card
status: experimental
description: |
  Prop-driven Person-Card fuer node.person card_quote view_mode.
  Horizontal Layout, rundes Foto links, Text rechts.
  Optional Contact-Links (tel/mailto/linkedin) als Stack unter Name.
  Rendert AUSSCHLIESSLICH den person-Variant von Figma 62110-27008;
  title-Variant ist out-of-scope (eigene SDC noetig).
props:
  type: object
  required: [name, funktion, photo]
  properties:
    name:           { type: string }
    funktion:       { type: string }
    photo:          { type: object, properties: { src, alt, width, height } }
    phone:          { type: string }      # optional in Schema, required im Content-Type
    email:          { type: string }
    linkedin_url:   { type: string }

3. Canvas-Code-Component CardPerson.jsx

Liegt unter web/themes/custom/byte_theme/canvas-components/CardPerson.jsx (1:1-Namens-Mapping zu card-person, kein PersonCard-Suffix-Flip — Customer-Lens-Empfehlung). Akzeptiert personNid als Prop, laedt node.person via JsonApiClient + SWR und rendert die SDC card-person mit den geladenen Feldern.

Editor-Workflow im MVP: Canvas-Page → Add Block → „Card · Person" → Person waehlen → Save → Publish.

4. MVP-Fallback fuer Vacancy-Detail

Eine globale Default-Person via config_pages.career_settings.field_default_recruiter (ER → node.person). Im Vacancy-Detail-Canvas-Template (node.vacancy.full) wird der CardPerson-Block mit dieser Default-NID auto-instanziert. Granulare Vacancy↔Person-Verknuepfung (Personio-Mapping per recruiter_id, ER-Feld pro Vacancy) ist deferred zu einer Folge-Story.

Konsistenz mit existierenden ADRs

  • Erweitert das Pattern aus ADR-0009 auf einen zweiten View-Mode-Use-Case (nach node.blog.card jetzt node.person.card_quote).
  • Vermeidet den in ADR-0004 verworfenen Conditional-Schema-Pattern (kein variant: blog|person-Switch auf bestehender card-SDC).
  • Respektiert ADR-0026 im MVP-Pfad (Vacancy bleibt Personio-only, Default kommt aus config_pages). Eine spaetere granulare Verknuepfung muss ADR-0026 verhandeln oder umgehen.

Rejected alternatives

Schmaler Scope: nur Recruiter-Card auf Vacancy-Detail, kein generischer Content-Type rejected

Variante A waere ein einmaliger Canvas-Block ohne node.person-Entity oder ein Paragraphs-Typ auf node.vacancy. Verworfen, weil der Story-Titel „Personen / Team Content Type" und die Sitemap-Node N33 („Ansprechpartner" als eigenstaendige Seite) breitere Wiederverwendung implizieren. Ohne generische Entity muesste eine Team-Seite spaeter ein zweites Datenmodell parallel aufbauen.

MVP-Scope auf alle drei View-Modes (card_quote + card_team + full) erweitern rejected

Eine Maximal-Variante wuerde Person-Detail-Page mit eigener Route (Sitemap N33), Team-Uebersicht und Vacancy-Sidebar in einer Story buendeln. Verworfen wegen Story-Groesse (geschaetzt 10+ Sub-Tasks) und MVP-Deadline-Risiko fuer 2026-07-08. Die nicht-priorisierten View-Modes (card_team, full) werden eigene Folge-Stories unter F-170.

Editorial-Override von field_recruiter auf node.vacancy (ADR-0026-Bruch) rejected

Ein dediziertes editorial-pflegbares field_recruiter auf node.vacancy wuerde ADR-0026 partiell brechen (Vacancy nicht mehr 100% Personio-only). Im MVP nicht noetig, weil ein globaler Default in config_pages ausreicht. Falls die Folge-Story granulare Pflege fordert, MUSS sie ADR-0026 explizit verhandeln (z.B. via Personio-Mapping auf recruiter_id, das ADR-0026 respektiert).

Drupal-Block-Plugin person_card_block statt Canvas-Code-Component rejected

Ein klassisches PHP-Block-Plugin im Drupal-Block-Layout-System waere robuster gegen Canvas-Disruptions, aber das Block-Layout-UI ist fuer Editor:innen clunky und kollidiert mit der Canvas-First-Strategie. Canvas-Code-Component bietet bessere Editor-UX. Kernel-Test-Coverage ist mit Canvas-Code-Components ebenfalls moeglich (Jest + Storybook).

Existierende card-SDC um Person-Variante erweitern (variant: blog|person) rejected

Bringt Conditional-Schema-Komplexitaet zurueck (heading_text nur sinnvoll wenn variant: blog, name nur sinnvoll wenn variant: person). ADR-0004 hat das analoge Pattern fuer Buttons explizit rejected. Schema-Validation kann Prop-Conditionality nicht enforcen, Twig-Template enthielte zwei verzweigte Render-Pfade.

Neuer card-quote-SDC mit dual-mode person|title (1:1 Figma-Treue) rejected

Wuerde den Figma-Frame 62110:27008 mit beiden Variants 1:1 abbilden. Verworfen, weil der title-Variant nicht MVP-Scope ist (keine konkrete Anwendung priorisiert) und Conditional-Schema das ADR-0004-Pattern verletzen wuerde. Falls der Editorial-Quote-Block spaeter priorisiert wird, bekommt er eine eigene SDC (z.B. card-quote-statement).

Card-Familie via abstrakter Basis-SDC konsolidieren (card-prop-driven mit kind: blog|person|event) rejected (deferred)

Customer-Lens-Gegenargument: „Eine weitere Card-Sibling-SDC ist Variant-Wildwuchs in Datei-Form. Wenn die Familie weiter waechst (card-event, card-testimonial, card-case-study), wird jede neue Story einen Sibling hinzufuegen." Verworfen-deferred: Abstraktion ist verfrueht, solange der konkrete Use-Case-Set unter 5 prop-driven View-Mode-Siblings bleibt. Die Schmerzschwelle „wann ist es zu viele Siblings?" wird in einer spaeteren ADR neu bewertet, sobald ein fuenfter prop-driven View-Mode-Card-Sibling vorgeschlagen wird.

Consequences

Direkte Folgen

  • Card-Familie waechst um eine prop-driven Variante. card-person ist der zweite prop-driven Sibling neben card (physisch vorhanden: 5 SDCs → 6; MAPPING-geplant zusaetzlich 2). Das ADR-0009-Pattern wird damit explizit als „mehrere Siblings pro View-Mode-Use-Case" interpretiert.
  • Inkonsistenz-Spannung mit Memory-Eintrag „Card-SDCs sollten Canvas-Wrapper sein". Diese ADR durchbricht die Regel bewusst fuer den Use-Case „Drupal-View-Mode-Listen-Render", analog zu ADR-0009. Beide Pattern existieren parallel, getrennt nach Render-Quelle.
  • Naming-Konvention 1:1 zwischen SDC und Canvas-Code-Component: card-person SDC und CardPerson.jsx Canvas-Component teilen den Wortstamm. Bei zukuenftigen Refactors keine Grep-Friction.
  • Vacancy-Detail-Layout aenderung sichtbar: Der Live-vertikale-Block (Name + Funktion + Tel als Stack) wird durch das horizontale Figma-Layout (Foto links, Position + Name rechts, Kontakt-Stack darunter) ersetzt. Diese visuelle Aenderung muss im PR-Body und im solcom-figma-reviewer-Output explizit dokumentiert sein.

Risiken / offene Verifikations-Punkte

  • Vacancy-Canvas-Template-Auto-Instanziierung (Customer-Lens-Finding SB-1): Im MVP-Fallback wird angenommen, dass canvas.canvas.node.vacancy.full.yml den CardPerson-Block mit der Default-NID automatisch in der Sidebar rendert. Vor Build verifizieren, dass das Canvas-Template diesen Eintrag enthaelt oder ergaenzt wird. Falls Editor pro Vacancy einen Block manuell setzen muss, ist der MVP-Workflow defekt und ADR-0026 ist in Frage gestellt.
  • config_pages-Pflege-UI (Customer-Lens-Finding SB-2): Editor:innen muessen config_pages.career_settings erreichen koennen. Sub-Task „Toolbar-Link / Editor-Onboarding fuer Career-Settings" wird im Story-Hub als Pflicht-Task ergaenzt.
  • Accessibility-Diff: Tel/Email/LinkedIn-Stack braucht korrekte tel:/mailto:-Schemas, aria-label fuer icon-only LinkedIn-Link und WCAG-2.1-AA-Kontrast. Diff-Zeit-Sache fuer den SDC-Reviewer.
  • SWR-Fetch-Performance: Pro CardPerson-Instance ein JSON:API-Request. Falls eine Page mehrere PersonCards rendert (Team-Uebersicht in einer Folge-Story), N+1-Risiko. Mitigation: Sammel-Request via filter[id-in]-Query, sobald relevant.
  • Datenschutz / DSGVO: Recruiter-Telefonnummern und -Email oeffentlich auf Vacancy-Detail. Klaerung mit Stakeholder vor Live-Schaltung empfohlen (eigener Reviewer-Strang, ausserhalb dieser ADR).

Folge-Stories (unter F-170)

  • Granulare Vacancy↔Person-Verknuepfung (ADR-pflichtig — Verhandlung mit ADR-0026, Personio-Mapping-Option).
  • View-Mode card_team + Team-Uebersichts-View (Drupal-View).
  • View-Mode full + Person-Detail-Page mit eigener Route (Sitemap-Node N33).
  • Bio-Feld + Rich-Editor fuer Person-Detail-Page.
  • Optional: title-Variant als eigenstaendige SDC (z.B. card-quote-statement fuer CEO-Zitate / Editorial-Statements).

Build-Workflow (Reihenfolge der Skills)

  1. solcom-design-mapping — MAPPING.html-Row fuer Frame 62110:27008 anlegen (heute fehlt sie).
  2. solcom-figma-to-sdc — SDC card-person bauen mit Re-Check gegen Figma bei jedem Schritt.
  3. drupal-canvas — Canvas-Code-Component CardPerson.jsx bauen + Workbench-Test.
  4. solcom-figma-reviewer — Visual-Compliance-Diff bei Mobile 375 / Tablet 768 / Desktop 1280, mit <section id="figma-extensions"> als Reference fuer intentional Drifts.