Motion-Slop: Warum jede KI-Landingpage dasselbe Fade-in-up animiert
Jeder Abschnitt hüpft 16 Pixel nach oben und blendet ein, 500 Millisekunden, ease-out, getriggert bei 30 Prozent Viewport. Fade-in-up ist das text-gray-600 der Animation. Hier ist, warum die Maschine genau hier landet und wie du Bewegung mit Absicht baust.
Scroll dich durch irgendeine Landingpage, die v0, Lovable oder Bolt in den letzten achtzehn Monaten ausgespuckt haben. Der Hero ist schon da. Dann scrollst du, und das Feature-Grid steigt 16 Pixel hoch und blendet ein. Weiterscrollen: die Testimonials steigen 16 Pixel hoch und blenden ein. Die Pricing-Cards steigen 16 Pixel hoch und blenden ein. Das FAQ-Accordion, die Footer-CTA, die Logo-Cloud: alle, dieselben 16 Pixel, dieselben 500 Millisekunden, dasselbe ease-out, derselbe Trigger bei 30 Prozent Viewport-Eintritt.
Das ist Fade-in-up. Es ist das text-gray-600 der Animation. Und wenn du es einmal gesehen hast, siehst du es überall: Jeder Abschnitt der Seite macht denselben kleinen Hüpfer, wie eine Revuetruppe, die nur einen einzigen Schritt kann.
Der exakte Code, weil es immer derselbe exakte Code ist
Hier die Framer-Motion-Variante. Wenn du dieses Jahr einen KI-Builder benutzt hast, hast du genau das wortwörtlich ausgeliefert:
const fadeInUp = {
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0 },
};
<motion.section
variants={fadeInUp}
initial="hidden"
whileInView="visible"
viewport={{ once: true }}
transition={{ duration: 0.5, ease: "easeOut" }}
>Mal y: 20, mal y: 16, gelegentlich y: 24. Die Dauer ist 0.5 oder 0.6. Das Easing ist easeOut, weil das das erste Easing in den Docs ist und das, was sich für ein Modell, das noch nie etwas gefühlt hat, "gut anfühlt". viewport={{ once: true }} steht drin, weil sich irgendwer auf Stack Overflow beschwert hat, dass die Animation beim Hochscrollen neu getriggert wurde, und diese Antwort steckt in den Trainingsdaten.
Die Nicht-React-Variante ist AOS, Animate On Scroll, und die ist noch nackter dabei:
<div data-aos="fade-up" data-aos-duration="600" data-aos-once="true">Oder der handgeklöppelte Intersection Observer, den ChatGPT schreibt, wenn du nach "Scroll-Animationen ohne Library" fragst:
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add("animate-in");
}
});
}, { threshold: 0.1 });
document.querySelectorAll(".reveal").forEach((el) => observer.observe(el));.reveal {
opacity: 0;
transform: translateY(20px);
transition: opacity 0.6s ease-out, transform 0.6s ease-out;
}
.reveal.animate-in {
opacity: 1;
transform: translateY(0);
}Drei verschiedene Stacks. Ein identisches Ergebnis: translateY(20px), 600ms, ease-out, einmal feuern bei 10 Prozent Threshold. Die Library wechselt, der Fingerabdruck nicht. Diese Konvergenz, verschiedene Tools, dasselbe Artefakt, ist die ganze These hinter warum jede KI-generierte Website gleich aussieht, und Motion ist einfach der Teil, den alle zu auditieren vergessen haben.
Warum ausgerechnet diese Animation zum Default wurde
Es ist kein Zufall, dass die Maschine hier landet. Fade-in-up ist das globale Minimum von "sicherer Bewegung", und jede Kraft drückt genau dorthin.
Es kann das Layout nicht zerschießen. Ein 16px-Nudge nach oben plus Opacity verursacht keinen Reflow, triggert keine horizontalen Scrollbars, läuft aus keinem Container über, kämpft nicht gegen das Grid. Eine KI, die auf "rendert beim ersten Versuch fehlerfrei" optimiert, greift zu der Transform, bei der man nichts falsch machen kann. width oder height zu animieren riskiert Layout-Thrashing. scale über einen gewissen Punkt clippt. translateY(20px) + opacity ist die eine Kombination ohne Fehlermodi, und praktischerweise auch die einzigen zwei Eigenschaften, die der Browser abseits des Main Threads auf dem GPU-Compositor animieren kann, sodass es nie ruckelt. Die Maschine ist über die billigste korrekte Antwort gestolpert.
Es ist das buchstäblich erste Beispiel in jeder Doku. Öffne die Framer-Motion-Startseite. Der whileInView-Abschnitt nutzt ein Fade-and-Rise. Die AOS-Demo führt mit fade-up. Der ScrollTrigger-Starter von GSAP ist ein y-Tween mit opacity. Modelle werden auf Dokumentation trainiert und auf den Blogposts, die Dokumentation abschreiben, also wird die meistdokumentierte Animation zur meistgenerierten. Das ist derselbe Mechanismus, der dafür sorgt, dass font-sans überall zu Inter auflöst, beschrieben in dem Typografie-Problem. Der Default in den Docs wird zum Default in der Welt.
Für Nicht-Designer liest es sich als "hochwertig". Für jemanden, der nicht artikulieren kann, warum, fühlt sich irgendeine Bewegung polierter an als gar keine. Das Modell weiß statistisch, dass "moderne Landingpage" zusammen mit "Scroll-Reveal" auftritt, also packt es das Reveal rein. Es weiß nicht, dass Bewegung eine Aufgabe hat. Es weiß, dass Bewegung mit dem Adjektiv korreliert, nach dem der Prompt gefragt hat.
Niemand widerspricht. Die Person, die "bau mir eine SaaS-Landingpage" promptet, hat keine Meinung zu Easing-Kurven. Sie sieht, dass sich Dinge bewegen, denkt "wirkt lebendig" und liefert aus. Die Feedback-Schleife, die uniforme Bewegung abstrafen würde, läuft nie an.
Warum uniforme Bewegung maschinell wirkt
Hier ist der Teil, den die Builder übersehen. Das Problem ist nicht Fade-in-up an sich. Ein einzelnes, gut platziertes Fade-up auf einer Hero-Headline ist in Ordnung, sogar gut. Das Problem ist, dass jedes Element dasselbe abbekommt.
Echtes Motion Design ist eine Hierarchie der Absicht. Die primäre Headline steigt und blendet ein. Die Subheadline folgt 80ms später, ein Stagger, weil sie untergeordnet ist. Der CTA-Button blendet gar nicht ein, er ist ab Frame eins da, vielleicht mit einem winzigen Scale-Settle, weil du willst, dass man ihn sofort klicken kann. Ein Produkt-Screenshot schiebt sich von der Seite herein, weil er die Bühne betritt. Ein Zahlen-Counter zählt hoch, weil die Bewegung *die* Information *ist*. Jede Entscheidung beantwortet eine Frage: Welche Rolle spielt dieses Element, und was soll die Bewegung darüber aussagen?
KI-Bewegung beantwortet keine einzige Frage. Sie wirft eine Transform auf ein .map() über deine Abschnitte. Die Testimonial-Card und der Rechtstext im Footer animieren identisch, weil sie für den Generator beide -Elemente in einem Array sind. Das ist das Verräterische. Menschen differenzieren, Maschinen iterieren. Wenn das Auge sieht, wie der Footer denselben Auftritt hinlegt wie der Hero, flaggt irgendein vorbewusster Teil des Gehirns: *Hier wurde nichts entschieden.* Es ist das Bewegungs-Äquivalent dazu, dass jede Card rounded-2xl mit shadow-sm ist, die Uniformität ist die Signatur. Die 23 Anzeichen für KI-generierten Code behandeln die statische Version davon, Motion ist dieselbe Krankheit in der Zeitdimension.
Darüber liegt ein zweites Verräterisches: der Stagger, der keiner ist. Wenn die KI doch mal Abwechslung versucht, staggert sie ein Grid mit transition={{ delay: index * 0.1 }}. Jetzt kaskadieren sechs Feature-Cards der Reihe nach herein. Sieht edler aus. Ist schlechter. 100ms Verzögerung pro Element bei sechs Elementen heißt, der Nutzer wartet 600ms auf die letzte Card, und die Kaskade hat keine Bedeutung: Card 6 ist nicht wichtiger als Card 1, warum kommt sie dann später an? Mechanischer Stagger ist Bewegung, die Choreografie cosplayt. Du erkennst es quer durch den Raum, weil der Rhythmus perfekt linear ist: 0, 100, 200, 300. Nichts in gutem Design läuft auf einem perfekt linearen Verzögerungsplan.
Und das Easing. ease-out auf allem. ease-out heißt schneller Start, langsames Ende, richtig für etwas, das *ankommt* und sich setzt. Aber die KI nutzt es auch für Exits, für Hovers, für alles, weil es der sichere Default ist. Echte Bewegung mischt: ease-out für Eingänge, ease-in für Ausgänge, ein eigenes cubic-bezier(0.34, 1.56, 0.64, 1) mit Overshoot für etwas Verspieltes, linear nur für Endlosschleifen. Eine Seite, auf der alles dieselbe Easing-Kurve teilt, hat den Tonumfang einer einzigen Klaviertaste.
So erkennst du es in 30 Sekunden
Öffne die DevTools, geh ins Animations-Panel (Chrome: More Tools → Animations) und scroll. Wenn jede aufgezeichnete Animation dieselbe Dauer, dasselbe Easing und dieselbe Transform-Distanz teilt, schaust du auf eine generierte Seite. Oder lies den Quelltext: durchsuch das Bundle nach whileInView, data-aos oder einer .reveal-Klasse mit translateY. Dann prüf: Ist es mit Absicht auf *ein* Element angewendet, oder per .map() über das ganze Dokument gestreut?
Die schnellere Heuristik, ganz ohne Tools: Scroll in normalem Lesetempo und frag dich, ob die Bewegung dir irgendetwas gesagt hat. Hat etwas deinen Blick auf das Wesentliche gelenkt? Oder ist die ganze Seite beim Scrollen der Reihe nach nach oben gezuckt? Wenn Letzteres, ist die Bewegung Dekoration, von einem Generator angeklatscht, ein weiterer Posten in der 30-Sekunden-Checkliste zur KI-Erkennung, gleich neben dem Blau-Lila-Gradient und der Geist-Schrift.
Noch ein Verräter: der Lade-Flash. Viele KI-Scroll-Reveal-Setups setzen opacity: 0 im CSS, triggern das Reveal aber in JS. Bei langsamer Verbindung oder mit deaktiviertem JS ist der Inhalt above the fold unsichtbar, eine leere Seite, bis der Observer feuert. Es ruiniert auch die gemessene Performance: Jedes Element, das nach dem First Paint hereinpoppt, zählt auf den Cumulative Layout Shift, und eine Seite, die ihren Hero hinter einem Observer versteckt, kann einen CLS jenseits von 0,25 hinlegen (Googles "schlecht"-Schwelle liegt bei 0,1) und ihren Largest-Contentful-Paint-Kandidaten komplett verlieren, weil das LCP-Element noch nicht gemalt war, als der Browser hinschaute. Eine echte Implementierung versteckt nie Inhalt hinter einem Script. Die generierte liefert fröhlich eine Seite aus, die für den First Paint eines Screenreaders und einen Lighthouse-Crawl leer ist, also genau die Art von Sache, die Googles Helpful-Content-Maschinerie zu bestrafen lernt.
Bewegung mit Absicht
Hier der Perspektivwechsel. Bevor du auch nur eine einzige Transition hinzufügst, beantworte pro animiertem Element eine Frage: Was ist die Aufgabe dieser Bewegung? Wenn du die Aufgabe nicht benennen kannst, lösch die Animation. "Sieht nett aus" ist keine Aufgabe.
Wenn Bewegung eine Aufgabe hat, hört der Code von ganz allein auf, uniform zu sein, weil verschiedene Aufgaben verschiedene Bewegung brauchen:
// Aufgabe: Den Blick auf die zentrale Aussage lenken, dann die
// stuetzende Zeile als nachgeordnet folgen lassen. Ein Stagger, gewollt.
<motion.h1
initial={{ opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, ease: [0.22, 1, 0.36, 1] }}
>
Halbiere deine AWS-Rechnung.
</motion.h1>
<motion.p
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.4, delay: 0.12 }} // folgt der Headline
>
Wir auditieren ungenutzte Instanzen, die du laengst vergessen hast.
</motion.p>
// Aufgabe: keine. Der CTA ist der Punkt. Er ist ab Frame eins da.
<a href="/start" className="btn-primary">Audit starten</a>Achte auf den Unterschied. Die Headline nutzt eine echte Easing-Kurve, [0.22, 1, 0.36, 1], ein schnelles Abbremsen, nicht das faule easeOut. Die Subheadline blendet nur ein, ohne y, weil sie keinen Auftritt hat, sie holt auf. Der CTA animiert gar nicht. Drei Elemente, drei Entscheidungen. Das ist das Gegenteil eines .map().
Für Scroll lautet die Regel: Enthülle etwas, das der Nutzer ohnehin nicht sehen konnte, und enthülle es, weil es jetzt wichtig ist, es zu sehen. Ein Chart, der seine Balken zeichnet, während er ins Viewport kommt, hier *ist* die Bewegung das Enthüllen der Daten, verdient. Eine Preistabelle, die beim Scrollen nichts tut, weil sie eine Tabelle ist und du sie lesen willst, nicht angucken, auch korrekt. Der generierte Instinkt ist "alles enthüllt sich". Der gestaltete Instinkt ist "fast nichts enthüllt sich, und das eine, das es tut, hat einen Grund".
Und mach die barrierefreie Variante ordentlich, in CSS, damit Inhalt nie hinter JS gesperrt ist und Reduced-Motion-Nutzer den Inhalt ohne Sprung bekommen:
.reveal { opacity: 0; transform: translateY(20px); transition: opacity .5s, transform .5s; }
.reveal.in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
.reveal { opacity: 1; transform: none; transition: none; }
}Dieser letzte Block ist die Zeile, die der Generator fast nie schreibt. Grob jeder dritte macOS- und iOS-Nutzer hat "Bewegung reduzieren" zeitweise aktiviert; ohne diese Media Query auszuliefern heißt, dass dein "hochwertiges" Reveal für sie nichts weiter ist als ein Layout, das ruckelt.
Ein paar praktische Anti-Slop-Moves:
- Variier nach Rolle, nicht nach Index. Kein
delay: i * 0.1. Stagger nur, wenn die Elemente eine echte Abfolge bilden, einen nummerierten Prozess, eine Timeline. Für ein Grid gleichgewichtiger Cards blende die ganze Gruppe gemeinsam ein, oder blende sie gar nicht ein. - Misch deine Easings. Eingänge bremsen ab. Ausgänge beschleunigen. Reservier Overshoot (
cubic-bezier(0.34, 1.56, 0.64, 1)) für ein, zwei verspielte Momente, damit es etwas Besonderes bleibt. Eine Seite mit drei eigenständigen Kurven wirkt komponiert; eine mit einer einzigen wirkt gedruckt. - Animier eine unerwartete Eigenschaft. Alle machen Opacity und Translate. Ein
clip-path-Wipe, einfilter: blur(8px), der sich scharfstellt, ein einzelner Akzent, der von#64748bzu#0ea5e9wandert, das liest sich gerade deshalb als bewusst, weil der Generator nie danach greift. - Kill das Below-the-fold-Reveal bei Langform-Content. Ein Artikel oder eine Docs-Seite braucht nicht, dass ihre Absätze beim Scrollen hochsteigen. Das ist Bewegung um der Bewegung willen, und es bremst das Lesen aktiv aus.
- Versteck nie Inhalt hinter JS. Bewegung ist eine Verbesserungsschicht, kein Tor. Sichtbar beim First Paint, immer.
Der tiefere Punkt zieht sich durch diesen ganzen Blog. Der KI-Default ist nicht schlecht, weil er hässlich ist, Fade-in-up ist für eine halbe Sekunde völlig angenehm. Er ist schlecht, weil er *undifferenziert* ist, und undifferenziert ist der eigentliche Fingerabdruck maschineller Ausgabe. Der Gradient, die Schrift, die rounded-2xl-Card, der text-gray-600-Fließtext und das 16px-Scroll-Reveal sind dasselbe Versagen: Ein Generator pickt die globale-Minimum-Sicherheitsentscheidung und wendet sie überall an, weil er keinen Geschmack zu riskieren und keine Absicht auszudrücken hat.
Du brichst es jedes Mal auf dieselbe Weise auf. Hör auf, das Tool entscheiden zu lassen. Nimm jedes Element in die Hand und frag, wofür es da ist. Die Bewegung, die diese Frage überlebt, ist die Bewegung, die es wert ist, ausgeliefert zu werden, und sie wird nie, niemals aussehen wie ein .map() von whileInView die Seite hinunter.
SHIP CODE THAT LOOKS INTENTIONAL
Scan your frontend for AI patterns. Generate a unique design system. Stop shipping the same blue gradient as everyone else.