Files
Anelia-Wolf/docs/superpowers/plans/2026-09-02-anelia-wolf-build-plan.md
2026-09-06 17:21:52 +02:00

14 KiB
Raw Blame History

Anelia Wolf Build-Plan & Komponenten-Vertrag

Stand: 2026-09-02 · Grundlage: docs/superpowers/specs/2026-09-02-anelia-wolf-website-design.md (Abschnitt A = verbindliche Abweichungen, danach die Jury-Synthese). Dieser Plan ist der Vertrag, nach dem parallel arbeitende Agenten bauen. Pfade, Exportnamen und Props sind verbindlich.

0. Bereits vorhanden (nicht neu anlegen, nur erweitern)

  • next.config.ts (static export, trailingSlash), src/app/globals.css (Tokens, .grain, .gold-text, .rule-gold, [data-theme]), src/app/layout.tsx (Fonts Jost/DM Mono/Bodoni Moda → --font-jost, --font-mono-face, --font-bodoni; Tailwind font-sans/font-mono/font-display; Skip-Link; SmoothScroll).
  • src/lib/site.ts (site, nav, legal), src/lib/utils.ts (cn), src/hooks/usePrefersReducedMotion.ts, src/components/providers/SmoothScroll.tsx (Lenis + GSAP-Ticker, window.lenis).
  • src/components/brand/WolfMark.tsx (SVG-Näherung des Monogramms; Klassen .wolfmark-v, .wolfmark-wolf, Pfade mit data-draw + pathLength="1", Gradient-Id ${idPrefix}wolf-gold; Props className, title, idPrefix, strokeWidth). Nicht verändern.
  • public/brand/README.md, public/photos/README.md (Drop-in-Slots), .claude/launch.json (anelia-dev, Port 3000).

Pakete: next 16.3, react 19.2, tailwindcss 4.3, motion 13 (motion/react), gsap 3.15 (gsap/ScrollTrigger, gsap/SplitText, gsap/DrawSVGPlugin alle frei), lenis 1.3, clsx, tailwind-merge. Nichts weiteres installieren außer server-only (falls gewünscht).

Tailwind v4: Utilities aus den Tokens: bg-ink bg-ink-2 bg-ivory bg-bone text-gold text-gold-deep text-grey-warm text-grey-deep border-line-dark border-line-light und Live-Tokens bg-bg text-fg text-fg-muted text-accent border-line bg-surface. Schrift: font-sans (Jost), font-mono (DM Mono), font-display (Bodoni Moda).

1. Globale Regeln für alle Agenten

  1. Next 16 App Router, statischer Export: Server-Komponenten standardmäßig; "use client" nur wo Hooks/DOM. Keine params/searchParams, keine Route Handler, keine fetch zur Laufzeit, kein localStorage/sessionStorage/ document.cookie, keine externen Skripte/iframes/Fonts. Externe Links target="_blank" rel="noopener noreferrer".
  2. Alle GSAP-Nutzung in useEffect/useLayoutEffect mit gsap.context() + ctx.revert() im Cleanup; Plugins nur über src/lib/gsap.ts. gsap.matchMedia() mit Bedingungen reduce: "(prefers-reduced-motion: reduce)", desktop: "(min-width: 1024px) and (pointer: fine)".
  3. Reduced motion: Endzustand sofort sichtbar. Ohne JS: Inhalte sichtbar (Initialzustände nur unter html.js setzen layout.tsx fügt ein 1-Zeilen-Inline-Script hinzu, das document.documentElement.classList.add("js") setzt).
  4. Bodoni Moda nie < 28 px; Gold-Text (text-gold, .gold-text) nur in data-theme="dark"; auf hell text-gold-deep. Radius 0 überall, keine Schatten, kein Bold (max. 500), ein Italic-Wort pro Headline (<em> in font-display italic).
  5. Alle Texte kommen aus src/content/*.ts kein Copy in JSX. Deutsche Anführungszeichen „…“, Gedankenstrich „–“.
  6. Ein <h1> pro Seite, Landmarken (header, main#inhalt, footer), Alt-Texte, sichtbarer Fokus.
  7. Jeder Agent arbeitet NUR in den ihm zugewiesenen Dateien. Fremde Dateien werden importiert, nicht editiert. Am Ende npx tsc --noEmit; Fehler in eigenen Dateien beheben. Fehlende Module aus dem Vertrag (noch nicht gebaut) sind der einzige erlaubte Fehlertyp und werden gemeldet.
  8. Kein next dev/next build durch Foundation-/Page-Agenten (nur tsc). Build und Browser-QA übernehmen die Integrations-/QA-Agenten.

2. Komponenten-Vertrag

2.1 src/lib/gsap.ts (F1)

export { gsap, ScrollTrigger, SplitText, DrawSVGPlugin };   // registriert, SSR-sicher (typeof window guard)
export const EASE = { out: "expo.out", inOut: "power3.inOut", none: "none" } as const;
export const DUR = { micro: 0.3, reveal: 0.6, lines: 0.9, door: 1.1, draw: 1.2, sheen: 0.8, rule: 0.9 } as const;
export const MM = { reduce: "(prefers-reduced-motion: reduce)", desktop: "(min-width: 1024px) and (pointer: fine)", touch: "(pointer: coarse)" } as const;

2.2 src/lib/site.ts (F2 erweitert)

export const flags = { showPlaceholders: true, showFaq: true, showStimmen: false, showSpeakerPdf: false,
  showSecondTalk: false, show24hPromise: false, showIndustry: false, showTwins: true } as const;
export const routes = { home: "/", coaching: "/leistungen/coaching/", trainings: "/leistungen/trainings/",
  keynotes: "/leistungen/keynotes/", impressum: "/impressum/", datenschutz: "/datenschutz/" } as const;
export const mailto = { kennenlernen: "mailto:info@anelia-wolf.com?subject=Kennenlerngespr%C3%A4ch",
  training: "mailto:info@anelia-wolf.com?subject=Training%20anfragen", keynote: "mailto:info@anelia-wolf.com?subject=Keynote%20anfragen" } as const;

2.3 src/lib/assets.ts (F2, nur in Server-Komponenten importieren)

export function hasPublicFile(relPath: string): boolean;              // fs.existsSync(path.join(process.cwd(), "public", relPath))
export function getPhotos(): { hero: string | null; portrait: string | null; stage: string | null };
export function getLogoSvg(): string | null;                          // Inhalt von public/brand/logo-mark.svg oder null

2.4 src/lib/legal.ts (F2)

export const LEGAL_TODO: string[];                                     // offene Pflichtangaben
export function ph(label: string): string;                             // liefert "[label]"  Platzhalter-Text

2.5 Content (F2) src/content/*.ts, alle as const, Typen exportieren

  • home.ts: hero, positioning, services, principle, about, audience, contact (+ termine) Copy 1:1 aus Spec §6.
  • coaching.ts, trainings.ts, keynotes.ts: { meta: {title, description}, masthead, eyebrow, h1: {before, em, after}, subline, cta: {label, href, suffix?, micro?}, facts: {label, value}[], situations: {h2, rows[]}, questions?, modules, rahmen, ablauf, faq, band: {h2, cta, micro} , ... } Copy 1:1 aus Spec §7.
  • legal.ts: Impressum- und Datenschutz-Abschnitte ({ h2, paragraphs[] }[]) aus docs/superpowers/specs/legal-templates.md, Platzhalter über ph(); kein OS-Plattform-Link; „Bezeichnung WOLF-Prinzip“.

2.6 Motion-Primitive (F1) src/components/motion/

<Rule variant?: "gold" | "line" = "line"; origin?: "left" | "center" | "right" | number /* px vom linken Rand */;
      play?: "view" | "load" = "view"; delay?: number; className?: string />           // 1px, scaleX 0→1, once
<Lines as?: "h1"|"h2"|"h3"|"p"|"div"; play?: "view" | "load"; delay?; stagger?; className?>children</Lines>
      // SplitText lines + mask; wartet document.fonts.ready (Timeout 1.5s); reduce → statisch; setzt aria-label
<Reveal as?; play?: "view"|"load"; delay?; y?=16; className?>children</Reveal>          // motion whileInView, once
<RevealGroup stagger?=0.06 delay? className?><RevealItem as? className?></RevealItem></RevealGroup>
<Draw play?: "load" | "view" | "scrub"; delay?; duration?; sheen?: boolean; onComplete?; className?>{svg}</Draw>
      // animiert alle [data-draw]-Pfade im Kind (stroke-dasharray/offset, pathLength=1); scrub: ScrollTrigger auf eigenem Element
<Door className? parallax?: boolean; priority?: boolean; src alt width height sizes? /> // <img> in clip-path-Maske, scale 1.06→1, 6% Parallax desktop
<ThemeTween />   // einmal pro Seite (template.tsx): tweent body-Variablen --bg --fg --fg-muted --accent --line --surface an [data-theme]-Grenzen (start "top 70%", end "top 30%", scrub 0.4)
<ScrollProgress />  // 1px --accent Linie unter dem Header
<Magnet strength?=0.3 className?>children</Magnet>   // nur desktop+no-preference, ref + ein rAF
<TransitionLink href label? className? LinkProps />  // Vorhang + router.push; Modifier/extern/Anker → normal
<Curtain />          // einmal in layout.tsx; hört auf transition-store
// src/components/motion/transition-store.ts: startTransition(href, label), subscribe(cb)

2.7 Brand (F1) src/components/brand/

<BrandMark className? idPrefix? variant?: "full" | "v-only" />   // Server: getLogoSvg() ? inline SVG (Pfade bekommen data-draw, Klassen wie WolfMark) : <WolfMark/>
<DrawMark play? sheen? handoff?: boolean className?>{<BrandMark/>}</DrawMark>  // Client-Hülle um BrandMark: Draw-Sequenz V-links → V-rechts → Wolf → (handoff-Linie nach unten) → Sheen
<Wordmark size?: "nav" | "footer"; href?="/" ; className? />                     // HTML-Wordmark ANELIA WOLF + Regel + Unterzeile

2.8 Layout (F1) src/components/layout/

<Header />        // Client; fixed; Zeile 1 Masthead (≥1024, kollabiert nach 80px), Zeile 2 Logo | 3 Nav-Links | Kontakt + Button (≥1280) | Menü (<1024)
<Masthead center: string; className? />   // drei Slots (Rolle | center | „Deutsch & Englisch“), Hairlines oben/unten
<OverlayMenu open onClose />               // Fullscreen, nummerierter Ledger, Fokus-Trap, Esc, inert
<Footer />        // Server; identisch auf allen Seiten
<Section id theme: "dark"|"light"; numeral?: string; total?: string; label?: string; className?; as?>children</Section>
      // <section data-theme id> mit Container + 12er-Grid; sticky Mono-Ziffer links (≥1024) bzw. Zeile oben (<1024); Kinder im Grid (col-span über className)
<Container className?>children</Container>  // max-w-[105rem] px-[clamp(20px,5vw,80px)]
<Folio current total next?: {label, href} className? />   // Hairline + „01 / 06“ + „Positionierung ↓“

src/app/template.tsx (F1): motion.main#inhalt opacity/y-Enter 0.6s, onAnimationComplete → ScrollTrigger.refresh(), mountet <ThemeTween/>, scrollt Lenis auf 0 bei Pfadwechsel. src/app/layout.tsx (F1 erweitert): Inline-Script html.js, <Header/>, {children}, <Footer/>, <Curtain/>, <ScrollProgress/>, MotionConfig reducedMotion="user".

2.9 UI / Editorial (F2) src/components/ui/

<Eyebrow className?>text</Eyebrow>                 // Jost 500 caps ls .28em text-accent, 24px-Tick ::before
<Numeral value="01" className? />                  // Bodoni 400 opsz 96, .gold-text auf dark / text-gold-700 auf light
<Marginalia items: {label: string; value: string; href?: string}[] className? />
<Ledger variant: "toc"|"werdegang"|"termine"|"situations"|"goals"; rows: LedgerRow[]; className? />
      // toc-Row: {numeral, title, oneLiner, keywords[], link:{label, href}}; werdegang: {keyword, text}; termine: {date, format, title, venue, href?};
      // situations: {numeral, text}; goals: {text}. toc-Zeile = eine <TransitionLink>; Hover ohne Reflow.
<ModuleTable modules: {title, goal, contents[], audience, meta}[] />   // Kopfzeile + <details> pro Modul, + → × per CSS
<SpecTable blocks: {title, goal, contents, audience, meta, tracks?: string[]}[] />
<Facts items: {label, value}[] className? />       // Mono-Stack mit Goldticks (Hero rechts oben)
<FactsStrip items: {label, value}[] />             // volle Breite, vertikale Hairlines (Speaker-Facts)
<Faq items: {q, a}[] />                             // native details/summary
<Steps items: {numeral, title, text}[] />
<ContactLinks />                                    // Schreib mir / Ruf an / Vernetzen (aus site.ts)
<Columns items: {label, title, lines[], link:{label, href}}[] offsetMiddle? />
<Formula lines: {op?: "+"|"=", text, em?: string, goldEm?: boolean, definition, delivers}[] signoff link />  // WOLF-Prinzip; Pin nur desktop
<Quote text author role? />
<Portrait src alt aspect?: "4/5"|"3/5"|"3/4"|"16/10"; caption? sticky? priority? className? />  // nutzt <Door/>; rendert null bei src null
<Button href label suffix? micro? variant?: "primary"|"ghost" external? className? />       // Outline, Sheen-Hover, Mono-Suffix aus Maske
<TextLink href label external? className? />       // caps + →, Unterstreichung wächst von links
<CtaBand h2: {before, em, after}; cta: ButtonProps; micro? />   // dunkles Band inkl. <ContactLinks/>
<Interruption text mono? />                          // 100svh ink, riesige Display-Zeile

2.10 Services gemeinsam (F2) src/components/services/

<ServiceHero content={coaching|trainings|keynotes} photo?: string|null />   // Masthead(center), Eyebrow, H1 mit <em>, Subline, Button, Facts rechts, Folio 01/03
<ServiceLayout>{children}</ServiceLayout>           // Sektionen hell mit Section-Wrapper; Kontakt-Band separat

2.11 Seiten (Phase 2)

  • src/app/page.tsx + src/components/home/{Hero,Positioning,ServicesIndex}.tsx (P1)
  • src/components/home/{WolfPrinciple,About,Audience,ProcessContact}.tsx (P2)
  • src/app/leistungen/coaching/page.tsx + src/components/services/coaching/* (P3)
  • src/app/leistungen/trainings/page.tsx + src/components/services/trainings/* (P4)
  • src/app/leistungen/keynotes/page.tsx + src/components/services/keynotes/* (P5)
  • src/app/impressum/page.tsx, src/app/datenschutz/page.tsx, src/app/not-found.tsx, src/app/sitemap.ts, src/app/robots.ts, src/app/icon.svg (P6)

3. Phasen

  1. Foundation (parallel): F1 = 2.1, 2.6, 2.7, 2.8 + template.tsx + layout.tsx-Erweiterung. F2 = 2.22.5, 2.9, 2.10 + src/hooks/useMediaQuery.ts.
  2. Integration 1: tsc + Verträge angleichen, npm run build grün mit Platzhalter-Startseite.
  3. Pages (parallel): P1P6.
  4. Integration 2: tsc, lint, build; alle 7 Routen im out/.
  5. Visual QA (Loop bis trocken, max. 3 Runden): Dev-Server über .claude/launch.json (anelia-dev), Screenshots 1440×900 + 390×844, Findings → Fix-Agenten (disjunkte Dateien) → erneut.
  6. Review: A11y, Performance/Static-Export, Datenschutz (keine externen Requests, kein Storage), Copy-Check gegen verbotene Phrasen → Fixes → finaler Build.