Communitygithub.com

motion-patterns

Produktionsreife Animationsmuster für React/Next.js – Button, Modal, Toast, Stagger, Seitenübergänge, Exit-Animationen, Scroll und Layout – basierend auf motion-foundations-Tokens und Federn.

Was ist motion-patterns?

Diese Skill bietet eine Sammlung von sofort einsetzbaren, kopierfertigen Animationsmustern für React- und Next.js-Anwendungen. Jedes Muster importiert Tokens und Federn aus der Abhängigkeit `motion-foundations`, sodass keine rohen Zahlenwerte oder eigenen Dauer- oder Easing-Werte definiert werden müssen. Die Skill deckt die häufigsten UI-Animationen ab: Button-Feedback, modale Dialoge, Toast-Stapel, gestaffelte Listeneingänge, Seitenübergänge mit dem Next.js App Router (inklusive `AnimatePresence` mit `mode="wait"`), Scroll-Enthüllungen, Scroll-verknüpfte Fortschrittsbalken, sich ausdehnende Karten und Akkordeons mit Layout-Animationen sowie Shared-Element-Übergänge mit `layoutId`. Sie stellt sicher, dass alle bedingten Renderings in `AnimatePresence` mit einem eindeutigen `key` verpackt sind, Exit-Animationen immer zusammen mit Enter-Animationen definiert werden und Barrierefreiheitsanforderungen (z. B. Focus Trap, Escape-Taste, `role="dialog"`) bei Modals erfüllt werden. Die Skill enthält ausführliche Entscheidungshilfen zur Auswahl des richtigen Musters, klare Anti-Patterns und ein vollständiges End-to-End-Beispiel, das Tokens, Federn und den Accessibility-Hook aus `motion-foundations` kombiniert.

Funktioniert mit~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/motion-patterns

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Motion Patterns

Kopier- und einfügbereite Muster für die häufigsten UI-Animationen. Jedes Muster basiert auf den Tokens und Federn von motion-foundations. Definiere hier keine neuen Dauer- oder Easing-Werte – importiere sie.

Wann aktivieren

  • Animieren eines Buttons, einer Karte, eines Modals oder einer Toast-Benachrichtigung
  • Erstellen von Listeneingängen mit Staffelung
  • Einrichten von Seitenübergängen im Next.js App Router
  • Hinzufügen von Ein- oder Ausgangsanimationen zu bedingten Inhalten
  • Implementieren von Scroll-Enthüllungen, scrollverknüpften Fortschrittsbalken oder klebrigen Story-Abschnitten
  • Erstellen von aufklappbaren Karten, Akkordeons oder Shared-Element-Übergängen

Ausgaben

Diese Skill produziert:

  • Barrierefreie, SSR-sichere Animationen für alle gängigen UI-Komponenten
  • Mit AnimatePresence umschlossene bedingte Renderings mit korrektem Exit-Verhalten
  • Page-Transition-Wrapper-Komponente für den Next.js App Router
  • Scroll-Enthüllungs- und Scroll-verknüpfte Muster mit useScroll + useTransform
  • Layout-Animationsmuster (layout, layoutId) für aufklappbare und überblendende Elemente

Prinzipien

  • Jedes Muster importiert aus motion-foundations. Keine rohen Zahlenwerte.
  • Jedes bedingte Rendern wird in AnimatePresence mit einem key verpackt.
  • Exit-Animationen werden immer zusammen mit Enter-Animationen definiert – niemals als nachträglicher Gedanke.
  • layout wird nur für kleine, isolierte Verschiebungen verwendet. Bei großen Teilbäumen werden explizite Transformationen verwendet.

Regeln

  1. Bedingte Renderings immer in AnimatePresence mit einem key auf dem direkten Kind umschließen. Ohne Key werden Exit-Animationen niemals ausgelöst.
  2. exit immer zusammen mit initial + animate definieren. Eine Animation ohne Exit ist unvollständig.
  3. mode="wait" bei Seitenübergängen verwenden. Enter darf erst beginnen, wenn Exit abgeschlossen ist.
  4. layout niemals auf Teilbäumen mit mehr als ~5 Kindern oder tief verschachteltem DOM verwenden. Stattdessen explizite x/y-Transformationen verwenden.
  5. Das Stagger-Intervall muss zwischen 0.05s und 0.10s liegen. Darunter wirkt es mechanisch; darüber träge.
  6. Modals müssen immer enthalten: Focus Trap, Escape-Taste zum Schließen, Scroll-Sperre, role="dialog", aria-modal="true".
  7. Scroll-Enthüllungen verwenden viewport={{ once: true }}. Wiederholungen beim Rausscrollen sind ablenkend, nicht informativ.
  8. Alle Token-Werte werden aus motion-foundations importiert. Keine Inline-Zahlen.

Entscheidungshilfen

Das richtige Muster auswählen

SituationMuster
Element erscheint / verschwindetAnimatePresence
Liste von Elementen, die nacheinander erscheinenStagger-Varianten
Navigation zwischen RoutenPage-Transition-Wrapper
Element ändert seine Größe an Ort und Stellelayout-Prop
Gleiches Element bewegt sich über Seitenkontexte hinweglayoutId
Element erscheint, wenn es in den sichtbaren Bereich gescrollt wirdwhileInView
Wert, der an die Scrollposition gebunden istuseScroll + useTransform

Wann mode="wait" vs. mode="sync" verwenden

ModusVerwendung wenn
waitSeitenübergänge, Inhaltswechsel (eines nach dem anderen)
syncGestapelte Benachrichtigungen, Listenelemente (Überlappung ist in Ordnung)
popLayoutElemente, die aus einer umfließenden Liste entfernt werden

Kernkonzepte

AnimatePresence-Vertrag

Drei Dinge müssen immer zutreffen:

  1. AnimatePresence umschließt das Bedingte
  2. Das direkte Kind hat einen key
  3. Das Kind hat eine exit-Prop

Fehlt eines davon, schlägt die Exit-Animation still fehl.

layout vs. layoutId

  • layout – animiert die eigene Größen-/Positionsänderung des Elements an Ort und Stelle
  • layoutId – verknüpft zwei separate Elemente und blendet sie über mehrere Renderings hinweg ineinander über

Verwende layout="position" für Text innerhalb eines sich ausdehnenden Containers, um zu verhindern, dass der Text-Umbruch animiert wird.

Code-Beispiele

Button-Feedback

"use client"
import { motion } from "motion/react"
import { springs, motionTokens } from "@/lib/motion-tokens"

<motion.button
  whileHover={{ scale: motionTokens.scale.pop }}
  whileTap={{ scale: motionTokens.scale.press }}
  transition={springs.snappy}
/>

Gestaffelte Liste

"use client"
import { motion } from "motion/react"
import { motionTokens, springs } from "@/lib/motion-tokens"

const container = {
  hidden: {},
  visible: {
    transition: {
      staggerChildren: 0.08,   // innerhalb der 0.05–0.10 Regel
      delayChildren: 0.1,
    },
  },
}

const item = {
  hidden:  { opacity: 0, y: motionTokens.distance.md },
  visible: { opacity: 1, y: 0, transition: springs.gentle },
}

<motion.ul variants={container} initial="hidden" animate="visible">
  {items.map((i) => (
    <motion.li key={i.id} variants={item} />
  ))}
</motion.ul>

Modal

"use client"
import { motion, AnimatePresence } from "motion/react"
import { motionTokens, springs } from "@/lib/motion-tokens"

// Am Aufrufort umschließen:
// <AnimatePresence>{isOpen && <Modal key="modal" />}</AnimatePresence>

export function Modal({ onClose }: { onClose: () => void }) {
  return (
    <>
      {/* Überlagerung */}
      <motion.div
        className="fixed inset-0 bg-black/50"
        initial={{ opacity: 0 }}
        animate={{ opacity: 1 }}
        exit={{ opacity: 0 }}
        onClick={onClose}
      />

      {/* Panel – Barrierefreiheitsanforderungen: Focus Trap, Escape zum Schließen,
          Scroll-Sperre, role="dialog", aria-modal="true" */}
      <motion.div
        role="dialog"
        aria-modal="true"
        className="fixed inset-x-4 top-1/2 -translate-y-1/2 rounded-xl bg-white p-6"
        initial={{
          opacity: 0,
          scale: motionTokens.scale.press,
          y: motionTokens.distance.sm,
        }}
        animate={{ opacity: 1, scale: 1, y: 0 }}
        exit={{
          opacity: 0,
          scale: motionTokens.scale.press,
          y: motionTokens.distance.sm,
        }}
        transition={springs.gentle}
      />
    </>
  )
}

Toast-Stapel

"use client"
import { motion, AnimatePresence } from "motion/react"
import { motionTokens, springs } from "@/lib/motion-tokens"

<AnimatePresence mode="sync">
  {toasts.map((t) => (
    <motion.div
      key={t.id}
      layout
      initial={{
        opacity: 0,
        x: motionTokens.distance.xl,
        scale: motionTokens.scale.subtle,
      }}
      animate={{ opacity: 1, x: 0, scale: 1 }}
      exit={{
        opacity: 0,
        x: motionTokens.distance.xl,
        scale: motionTokens.scale.subtle,
      }}
      transition={springs.snappy}
    />
  )}
</AnimatePresence>

Seitenübergang (Next.js App Router)

// components/page-transition.tsx
"use client"
import { motion, AnimatePresence } from "motion/react"
import { usePathname } from "next/navigation"
import { motionTokens } from "@/lib/motion-tokens"

const variants = {
  initial: { opacity: 0, y: motionTokens.distance.sm },
  enter:   { opacity: 1, y: 0 },
  exit:    { opacity: 0, y: -motionTokens.distance.sm },
}

export function PageTransition({ children }: { children: React.ReactNode }) {
  const pathname = usePathname()
  return (
    <AnimatePresence mode="wait">
      <motion.div
        key={pathname}
        variants={variants}
        initial="initial"
        animate="enter"
        exit="exit"
        transition={{
          duration: motionTokens.duration.normal,
          ease: motionTokens.easing.smooth,
        }}
      >
        {children}
      </motion.div>
    </AnimatePresence>
  )
}

Scroll-Enthüllung

"use client"
import { motion } from "motion/react"
import { motionTokens, springs } from "@/lib/motion-tokens"

<motion.div
  initial={{ opacity: 0, y: motionTokens.distance.lg }}
  whileInView={{ opacity: 1, y: 0 }}
  viewport={{ once: true, margin: "-80px" }}   // once: true – Regel 7
  transition={{ duration: motionTokens.duration.slow, ease: motionTokens.easing.smooth }}
/>

Scroll-Fortschrittsbalken

"use client"
import { motion, useScroll } from "motion/react"

export function ScrollProgress() {
  const { scrollYProgress } = useScroll()
  return (
    <motion.div
      className="fixed top-0 left-0 h-1 bg-indigo-500 origin-left w-full"
      style={{ scaleX: scrollYProgress }}
    />
  )
}

Aufklappbare Karte

"use client"
import { useState } from "react"
import { motion, AnimatePresence } from "motion/react"
import { springs, motionTokens } from "@/lib/motion-tokens"

export function ExpandingCard({ title, body }: { title: string; body: string }) {
  const [expanded, setExpanded] = useState(false)

  return (
    <motion.div layout onClick={() => setExpanded(!expanded)} className="cursor-pointer">
      {/* layout="position" verhindert das Animieren des Text-Umbruchs */}
      <motion.h2 layout="position" className="font-semibold">
        {title}
      </motion.h2>

      <AnimatePresence>
        {expanded && (
          <motion.p
            key="body"
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            transition={{ duration: motionTokens.duration.fast }}
          >
            {body}
          </motion.p>
        )}
      </AnimatePresence>
    </motion.div>
  )
}

Shared-Element-Überblendung

// Quellkontext
<motion.img layoutId="hero-image" src={src} className="w-16 h-16 rounded" />

// Zielkontext (gleiches layoutId – motion übernimmt den Übergang)
<motion.img layoutId="hero-image" src={src} className="w-full rounded-xl" />

Akkordeon

<motion.div
  initial={false}
  animate={{ opacity: open ? 1 : 0, scaleY: open ? 1 : 0 }}
  style={{ transformOrigin: "top", overflow: "hidden" }}
  transition={{
    duration: motionTokens.duration.normal,
    ease: motionTokens.easing.smooth,
  }}
> {children}
</motion.div>

End-to-End-Beispiel

Eine gestaffelte Liste, die beim Mounten erscheint, bedingtes Vorhandensein behandelt und reduzierte Bewegung respektiert – Kombination von Tokens, Federn, AnimatePresence und dem Accessibility-Hook aus motion-foundations:

"use client"
import { useState } from "react"
import { motion, AnimatePresence } from "motion/react"
import { motionTokens, springs } from "@/lib/motion-tokens"
import { useSafeMotion } from "@/hooks/use-reduced-motion"

const containerVariants = {
  hidden: {},
  visible: {
    transition: { staggerChildren: 0.08, delayChildren: 0.1 },
  },
}

function ListItem({ label, onRemove }: { label: string; onRemove: () => void }) {
  const safe = useSafeMotion(motionTokens.distance.sm)
  return (
    <motion.li
      variants={{
        hidden:  safe.initial,
        visible: safe.animate,
      }}
      exit={safe.exit}
      transition={springs.gentle}
      className="flex items-center justify-between p-3 rounded-lg bg-white shadow-sm"
    >
      <span>{label}</span>
      <button onClick={onRemove}>Entfernen</button>
    </motion.li>
  )
}

export function AnimatedList({ items, onRemove }: {
  items: { id: string; label: string }[]
  onRemove: (id: string) => void
}) {
  return (
    <motion.ul
      variants={containerVariants}
      initial="hidden"
      animate="visible"
      className="space-y-2"
    >
      <AnimatePresence mode="popLayout">
        {items.map((item) => (
          <ListItem
            key={item.id}
            label={item.label}
            onRemove={() => onRemove(item.id)}
          />
        ))}
      </AnimatePresence>
    </motion.ul>
  )
}

Einschränkungen / Nicht-Ziele

Diese Skill behandelt nicht:

  • Token- und Feder-Definitionen → siehe motion-foundations
  • Drag-Interaktionen, Wischgesten, neu anordenbare Listen → siehe motion-advanced
  • Textanimationen (Wort-/Zeichen-Enthüllung, Zähler) → siehe motion-advanced
  • SVG-Pfadzeichnen oder -Morphing → siehe motion-advanced
  • Benutzerdefinierte Animations-Hooks → siehe motion-advanced
  • CSS-only-Transitionen ohne motion/react

Anti-Patterns

Anti-PatternVerletzte RegelBehebung
AnimatePresence-Kind ohne keyRegel 1Stabilen key zum direkten Kind hinzufügen
initial + animate ohne exitRegel 2Immer alle drei zusammen definieren
Seitenübergang ohne mode="wait"Regel 3mode="wait" zu AnimatePresence hinzufügen
layout auf einer 50-Elemente-ListeRegel 4mode="popLayout" oder explizite Transformationen verwenden
staggerChildren: 0.2 auf einer 10-Elemente-ListeRegel 5Auf 0.08–0.10 begrenzen
Modal ohne Focus TrapRegel 6focus-trap-react oder Radix Dialog hinzufügen
whileInView ohne viewport={{ once: true }}Regel 7Wiederholte Eintritte lenken ab, informieren nicht
transition={{ duration: 0.3 }} inlineRegel 8motionTokens.duration.normal verwenden

Verwandte Skills

  • motion-foundations – definiert alle Tokens, Federn, den useSafeMotion-Hook und SSR-Guards, die jedes Muster hier importiert. Muss zuerst eingerichtet werden.
  • motion-advanced – erweitert diese Muster um Drag, Gesten, SVG, Text, benutzerdefinierte Hooks und imperative Ablaufsteuerung. Definiert keine Muster aus dieser Skill neu.

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

Verwandte Skills