14 KiB
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; Tailwindfont-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 mitdata-draw+pathLength="1", Gradient-Id${idPrefix}wolf-gold; PropsclassName,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
- Next 16 App Router, statischer Export: Server-Komponenten standardmäßig;
"use client"nur wo Hooks/DOM. Keineparams/searchParams, keine Route Handler, keinefetchzur Laufzeit, keinlocalStorage/sessionStorage/document.cookie, keine externen Skripte/iframes/Fonts. Externe Linkstarget="_blank" rel="noopener noreferrer". - Alle GSAP-Nutzung in
useEffect/useLayoutEffectmitgsap.context()+ctx.revert()im Cleanup; Plugins nur übersrc/lib/gsap.ts.gsap.matchMedia()mit Bedingungenreduce: "(prefers-reduced-motion: reduce)",desktop: "(min-width: 1024px) and (pointer: fine)". - Reduced motion: Endzustand sofort sichtbar. Ohne JS: Inhalte sichtbar (Initialzustände nur unter
html.jssetzen –layout.tsxfügt ein 1-Zeilen-Inline-Script hinzu, dasdocument.documentElement.classList.add("js")setzt). - Bodoni Moda nie < 28 px; Gold-Text (
text-gold,.gold-text) nur indata-theme="dark"; auf helltext-gold-deep. Radius 0 überall, keine Schatten, kein Bold (max. 500), ein Italic-Wort pro Headline (<em>infont-display italic). - Alle Texte kommen aus
src/content/*.ts– kein Copy in JSX. Deutsche Anführungszeichen „…“, Gedankenstrich „–“. - Ein
<h1>pro Seite, Landmarken (header,main#inhalt,footer), Alt-Texte, sichtbarer Fokus. - 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. - Kein
next dev/next builddurch Foundation-/Page-Agenten (nurtsc). 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[] }[]) ausdocs/superpowers/specs/legal-templates.md, Platzhalter überph(); 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
- Foundation (parallel): F1 = 2.1, 2.6, 2.7, 2.8 +
template.tsx+layout.tsx-Erweiterung. F2 = 2.2–2.5, 2.9, 2.10 +src/hooks/useMediaQuery.ts. - Integration 1:
tsc+ Verträge angleichen,npm run buildgrün mit Platzhalter-Startseite. - Pages (parallel): P1–P6.
- Integration 2:
tsc,lint,build; alle 7 Routen imout/. - 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. - Review: A11y, Performance/Static-Export, Datenschutz (keine externen Requests, kein Storage), Copy-Check gegen verbotene Phrasen → Fixes → finaler Build.