Option
HeimHeim Skill Datenwissenschaft und ML motion-foundations

motion-foundations

affaan-m/ECC affaan-m/ECC

Bewegungstoken, Feder-Voreinstellungen, Performance-Regeln, Geräteanpassung, Durchsetzung der Barrierefreiheit und SSR-Sicherheit für React/Next.js unter Verwendung von motion/react. Die Grundlagen – alle weiteren Motion-Kenntnisse bauen darauf auf.

...Alle erweitern
51
Zeit aktualisiert 29. Juli 2026

Grundlagen des Bewegungssystems

Die Basisebene des Bewegungssystems. Definiert alle Werte, Einschränkungen und Regeln, die nachgelagerte Fähigkeiten (motion-patterns, motion-advanced) erben. Laden Sie diese Funktion, bevor jegliche Animationsarbeiten beginnen.

Wann aktivieren?

  • Beim Starten einer animierten Komponente von Grund auf
  • Einrichten von Tokens, Feder-Voreinstellungen oder Easing-Werten
  • Implementierung prefers-reduced-motion Unterstützung
  • Fehlerbehebung bei Hydration-Abweichungen von den Anfangszuständen der Animation
  • Prüfen, ob eine Animation überhaupt vorhanden sein sollte

Ergebnisse

Diese Funktion liefert:

  • Ein gemeinsames motionTokens Objekt (Dauer, Beschleunigung, Entfernung, Skalierung)
  • Eine gemeinsam genutzte springs gemeinsame Voreinstellungszuordnung (5 benannte Konfigurationen)
  • Ein shouldAnimate() von allen Komponenten verwendetes Gate
  • Barrierefreiheitskonforme Standard-Animationen über useReducedMotion
  • SSR-sichere Anfangszustände ohne Hydration-Warnungen

Grundsätze

Eine Bewegung muss mindestens eine der folgenden Bedingungen erfüllen oder sie muss entfernt werden:

  • Die Aufmerksamkeit lenken
  • Den Status vermitteln
  • Räumliche Kontinuität wahren

Reaktionsfähigkeit hat immer Vorrang vor Laufruhe. Eine Animation mit 60 fps, die zu Eingabeverzögerungen führt, ist schlechter als gar keine Animation.

Regeln

Diese sind nicht verhandelbar. Sie gelten für jede Komponente im System.

  1. Verwenden Sie ausschließlich „motion/react“. Importieren Sie niemals aus framer-motion. Mischen Sie niemals beides in derselben Baumstruktur.
  2. initial muss mit der Serverausgabe übereinstimmen. Wenn der Server opacity: 1, muss das initial Prop ebenfalls opacity: 1. Keine Ausnahmen.
  3. Reduzierte Bewegung hat Vorrang vor allem anderen. Wenn useReducedMotion() gibt true oder prefersReduced ist true, werden alle Transformationen deaktiviert. Als einzige Ausnahmeregelung sind Fade-Effekte, die ausschließlich die Deckkraft betreffen und ≤ 0,2 s dauern, zulässig.
  4. Layout-Eigenschaften dürfen niemals animiert werden. width, height, top, left, margin, padding sind in animate. Verwenden Sie transform und opacity .
  5. Alle Token-Werte stammen aus „motionTokens“. Fest codierte Dauerangaben und Beschleunigungsverläufe in Komponentendateien sind verboten.
  6. Alle Spring-Konfigurationen stammen aus der Map „springs“. Inline- stiffness/damping Werte sind verboten.
  7. "use client" ist in jeder Datei erforderlich, die aus motion/react.
  8. Lesen Sie niemals window oder navigator auf Modulebene. Verwenden Sie stets typeof window !== "undefined".

Leitfaden zur Entscheidungsfindung

Auswahl einer Dauer

Auswahl einer Feder

Wann sollte die Animation vollständig deaktiviert werden?

Deaktivieren (so, dass shouldAnimate() Rückkehr false) in folgenden Fällen:

  • prefersReduced ist true
  • isLowEnd ist true und die Animation nicht wesentlich ist
  • Das Element befindet sich außerhalb des Bildschirms und wird niemals in den Ansichtsbereich gelangen
  • Die Animation ist rein dekorativ und hat keinen UX-Zweck

Kernkonzepte

Token-System

// lib/motion-tokens.ts
export const motionTokens = {
  duration: {
    instant: 0.08,
    fast:    0.18,
    normal:  0.35,
    slow:    0.6,
    crawl:   1.0,
  },
  easing: {
    smooth: [0.22, 1, 0.36, 1],
    sharp:  [0.4, 0, 0.2, 1],
    bounce: [0.34, 1.56, 0.64, 1],
    linear: [0, 0, 1, 1],
  },
  distance: {
    xs: 4,
    sm: 8,
    md: 16,
    lg: 24,
    xl: 48,
  },
  scale: {
    subtle: 0.98,
    press:  0.95,
    pop:    1.04,
  },
}

export const springs = {
  snappy:  { type: "spring", stiffness: 300, damping: 30 },
  gentle:  { type: "spring", stiffness: 120, damping: 14 },
  bouncy:  { type: "spring", stiffness: 400, damping: 10 },
  instant: { type: "spring", stiffness: 600, damping: 35 },
  release: { type: "spring", stiffness: 200, damping: 20, restDelta: 0.001 },
}

Laufzeit-Flags

// lib/motion-config.ts
export const motionConfig = {
  isLowEnd() {
    return (
      typeof navigator !== "undefined" &&
      navigator.hardwareConcurrency <= 4
    )
  },

  prefersReduced() {
    return (
      typeof window !== "undefined" &&
      window.matchMedia("(prefers-reduced-motion: reduce)").matches
    )
  },

  shouldAnimate({ essential = false } = {}) {
    if (this.prefersReduced()) return false
    if (!essential && this.isLowEnd()) return false
    return true
  },

  duration() {
    return this.isLowEnd() || this.prefersReduced()
      ? motionTokens.duration.instant
      : motionTokens.duration.normal
  },
}

Barrierefreiheit

Prioritätsreihenfolge (von der höchsten zur niedrigsten):

  1. prefers-reduced-motion: reduce — deaktiviert alle Transformationen, begrenzt Opazitätsübergänge auf ≤ 0,2 s
  2. Erkennung von Geräten mit geringer Leistung — verkürzt die Dauer, entfernt nicht wesentliche Animationen
  3. Designpräferenz — alles andere

Bewegungen müssen sanft abklingen. Sie dürfen niemals abrupt verschwinden, sodass es zu Layoutverschiebungen kommt oder die Orientierung beeinträchtigt wird.

// hooks/use-reduced-motion.tsx
"use client"
import { useReducedMotion } from "motion/react"

export function useSafeMotion(fullY: number = 16) {
  const reduce = useReducedMotion()
  return {
    initial: { opacity: 0, y: reduce ? 0 : fullY },
    animate: { opacity: 1, y: 0 },
    exit:    { opacity: 0, y: reduce ? 0 : -fullY },
  }
}
/* globals.css */
@media (prefers-reduced-motion: reduce) {
  .motion-safe-transition  { transition: opacity 0.15s; }
  .motion-reduce-transform { transform: none !important; }
}

<div class="motion-safe:animate-fade motion-reduce:opacity-100">div>

SSR-/Hydration-Sicherheit

Regel: „initial“ muss immer mit dem übereinstimmen, was der Server rendert.

// WRONG — server renders opacity:1 but initial says 0 → hydration mismatch
div initial={{ opacity: 0 }} animate={{ opacity: 1 }} />

// CORRECT — use AnimatePresence or defer to client mount
"use client"
const [mounted, setMounted] = useState(false)
useEffect(() => setMounted(true), [])

div
  initial={{ opacity: mounted ? 0 : 1 }}
  animate={{ opacity: 1 }}
/>

Code-Beispiele

End-to-End: Tokens + Springs + Barrierefreiheit + SSR-Guard

// components/fade-in-card.tsx
"use client"

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

interface FadeInCardProps {
  children: React.ReactNode
  delay?: number
}

export function FadeInCard({ children, delay = 0 }: FadeInCardProps) {
  // SSR guard — initial must match server output (opacity: 1)
  const [mounted, setMounted] = useState(false)
  useEffect(() => setMounted(true), [])

  // Accessibility — disables transform when reduced motion is preferred
  const safeMotion = useSafeMotion(motionTokens.distance.md)

  // Device gate — skip animation on low-end hardware
  if (!motionConfig.shouldAnimate() || !mounted) {
    return <div>{children}div>
  }

  return (
    <motion.div
      initial={safeMotion.initial}
      animate={safeMotion.animate}
      exit={safeMotion.exit}
      transition={{
        ...springs.gentle,
        delay,
      }}
      whileHover={{ scale: motionTokens.scale.pop }}
      whileTap={{ scale: motionTokens.scale.press }}
    >
      {children}
    motion.div>
  )
}

Einschränkungen / Nicht-Ziele

Diese Fertigkeit umfasst nicht:

  • UI-Komponentenmuster (Schaltflächen, Modals, Stagger) → siehe motion-patterns
  • Drag, Gesten, SVG, Textanimationen, benutzerdefinierte Hooks → siehe motion-advanced
  • Reine CSS-Animationen oder Tailwind- animate-* Klassen ohne motion/react
  • Animationsbibliotheken von Drittanbietern (GSAP, anime.js usw.)
  • Entscheidungen zum Motion Design (wann animiert werden soll, was hervorgehoben werden soll) – das ist eine gestalterische Frage, keine Einschränkung durch den Code

Anti-Muster

Verwandte Fähigkeiten

  • motion-patterns — verwendet die hier definierten Tokens und Springs, um Muster für Schaltflächen, Modal-Fenster, Stagger-Effekte, Seitenübergänge und Scroll-Effekte zu erstellen. Es werden keine Werte neu definiert.
  • motion-advanced — Verwendet die hier definierten Tokens und Springs für Drag-, SVG-, Text- und Gestenmuster. Fügt useAnimate Sequenzen und benutzerdefinierte Hooks auf dieser Grundlage hinzu.
Auf GitHub ansehen

Motion Foundations

The base layer of the motion system. Defines every value, constraint, and rule that downstream skills (motion-patterns, motion-advanced) inherit. Load this skill before any animation work begins.

When to Activate

  • Starting any animated component from scratch
  • Setting up tokens, spring presets, or easing values
  • Implementing prefers-reduced-motion support
  • Debugging hydration mismatches from animation initial states
  • Evaluating whether an animation should exist at all

Outputs

This skill produces:

  • A shared motionTokens object (duration, easing, distance, scale)
  • A shared springs preset map (5 named configs)
  • A shouldAnimate() gate used by all components
  • Accessibility-compliant animation defaults via useReducedMotion
  • SSR-safe initial states with zero hydration warnings

Principles

Motion must do at least one of the following or it must be removed:

  • Guide attention
  • Communicate state
  • Preserve spatial continuity

Responsiveness always outranks smoothness. A 60 fps animation that causes input delay is worse than no animation.

Rules

These are non-negotiable. They apply to every component in the system.

  1. Use motion/react only. Never import from framer-motion. Never mix the two in the same tree.
  2. initial must match server output. If the server renders opacity: 1, the initial prop must also be opacity: 1. No exceptions.
  3. Reduced motion overrides everything. When useReducedMotion() returns true or prefersReduced is true, all transforms are disabled. Opacity-only fades at ≤ 0.2s are the only permitted fallback.
  4. Never animate layout properties. width, height, top, left, margin, padding are banned from animate. Use transform and opacity only.
  5. All token values come from motionTokens. Hardcoded durations and easings in component files are forbidden.
  6. All spring configs come from the springs map. Inline stiffness/damping values are forbidden.
  7. "use client" is required on every file that imports from motion/react.
  8. Never read window or navigator at module level. Always guard with typeof window !== "undefined".

Decision Guidance

Choosing a duration

Choosing a spring

When to disable animation entirely

Disable (make shouldAnimate() return false) when:

  • prefersReduced is true
  • isLowEnd is true and the animation is non-essential
  • The element is off-screen and will never enter the viewport
  • The animation is purely decorative with no UX purpose

Core Concepts

Token system

// lib/motion-tokens.ts
export const motionTokens = {
  duration: {
    instant: 0.08,
    fast:    0.18,
    normal:  0.35,
    slow:    0.6,
    crawl:   1.0,
  },
  easing: {
    smooth: [0.22, 1, 0.36, 1],
    sharp:  [0.4, 0, 0.2, 1],
    bounce: [0.34, 1.56, 0.64, 1],
    linear: [0, 0, 1, 1],
  },
  distance: {
    xs: 4,
    sm: 8,
    md: 16,
    lg: 24,
    xl: 48,
  },
  scale: {
    subtle: 0.98,
    press:  0.95,
    pop:    1.04,
  },
}

export const springs = {
  snappy:  { type: "spring", stiffness: 300, damping: 30 },
  gentle:  { type: "spring", stiffness: 120, damping: 14 },
  bouncy:  { type: "spring", stiffness: 400, damping: 10 },
  instant: { type: "spring", stiffness: 600, damping: 35 },
  release: { type: "spring", stiffness: 200, damping: 20, restDelta: 0.001 },
}

Runtime flags

// lib/motion-config.ts
export const motionConfig = {
  isLowEnd() {
    return (
      typeof navigator !== "undefined" &&
      navigator.hardwareConcurrency <= 4
    )
  },

  prefersReduced() {
    return (
      typeof window !== "undefined" &&
      window.matchMedia("(prefers-reduced-motion: reduce)").matches
    )
  },

  shouldAnimate({ essential = false } = {}) {
    if (this.prefersReduced()) return false
    if (!essential && this.isLowEnd()) return false
    return true
  },

  duration() {
    return this.isLowEnd() || this.prefersReduced()
      ? motionTokens.duration.instant
      : motionTokens.duration.normal
  },
}

Accessibility

Priority order (highest to lowest):

  1. prefers-reduced-motion: reduce — disables all transforms, limits opacity transitions to ≤ 0.2s
  2. Low-end device detection — reduces duration, removes non-essential animations
  3. Design preference — everything else

Motion must degrade gracefully. It must never disappear abruptly in a way that causes layout shift or confuses orientation.

// hooks/use-reduced-motion.tsx
"use client"
import { useReducedMotion } from "motion/react"

export function useSafeMotion(fullY: number = 16) {
  const reduce = useReducedMotion()
  return {
    initial: { opacity: 0, y: reduce ? 0 : fullY },
    animate: { opacity: 1, y: 0 },
    exit:    { opacity: 0, y: reduce ? 0 : -fullY },
  }
}
/* globals.css */
@media (prefers-reduced-motion: reduce) {
  .motion-safe-transition  { transition: opacity 0.15s; }
  .motion-reduce-transform { transform: none !important; }
}
<!-- Tailwind -->
<div class="motion-safe:animate-fade motion-reduce:opacity-100"></div>

SSR / hydration safety

Rule: initial must always match what the server renders.

// WRONG — server renders opacity:1 but initial says 0 → hydration mismatch
<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} />

// CORRECT — use AnimatePresence or defer to client mount
"use client"
const [mounted, setMounted] = useState(false)
useEffect(() => setMounted(true), [])

<motion.div
  initial={{ opacity: mounted ? 0 : 1 }}
  animate={{ opacity: 1 }}
/>

Code Examples

End-to-end: tokens + springs + accessibility + SSR guard

// components/fade-in-card.tsx
"use client"

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

interface FadeInCardProps {
  children: React.ReactNode
  delay?: number
}

export function FadeInCard({ children, delay = 0 }: FadeInCardProps) {
  // SSR guard — initial must match server output (opacity: 1)
  const [mounted, setMounted] = useState(false)
  useEffect(() => setMounted(true), [])

  // Accessibility — disables transform when reduced motion is preferred
  const safeMotion = useSafeMotion(motionTokens.distance.md)

  // Device gate — skip animation on low-end hardware
  if (!motionConfig.shouldAnimate() || !mounted) {
    return <div>{children}</div>
  }

  return (
    <motion.div
      initial={safeMotion.initial}
      animate={safeMotion.animate}
      exit={safeMotion.exit}
      transition={{
        ...springs.gentle,
        delay,
      }}
      whileHover={{ scale: motionTokens.scale.pop }}
      whileTap={{ scale: motionTokens.scale.press }}
    >
      {children}
    </motion.div>
  )
}

Constraints / Non-Goals

This skill does not cover:

  • UI component patterns (button, modal, stagger) → see motion-patterns
  • Drag, gestures, SVG, text animations, custom hooks → see motion-advanced
  • CSS-only animations or Tailwind animate-* classes without motion/react
  • Third-party animation libraries (GSAP, anime.js, etc.)
  • Motion design decisions (when to animate, what to emphasize) — that is a design concern, not a code constraint

Anti-Patterns

Related Skills

  • motion-patterns — consumes tokens and springs defined here to build button, modal, stagger, page transition, and scroll patterns. Does not redefine any values.
  • motion-advanced — consumes tokens and springs defined here for drag, SVG, text, and gesture patterns. Adds useAnimate sequences and custom hooks on top of this foundation.

Alle Dateien

1 Dateien

motion-foundations installieren

Laden Sie die Skill-Dateien herunter und entpacken Sie sie in Ihr Verzeichnis „.claude/skills/“.

ZIP herunterladen

Klonen Sie das Repository und kopieren Sie die Skill-Dateien in Ihr Projekt.

git clone https://github.com/affaan-m/ECC/tree/main/skills/motion-foundations # Copy the skill folder to .claude/skills/ or .codex/skills/

Kopieren Kopieren
Schnelle Einrichtung: Kopiere den Skill-Ordner nach .claude/skills/. Claude erkennt den Skill automatisch und nutzt ihn.
Repository affaan-m/ECC

Ähnliche Skills

web-search
Zeit aktualisiert 29. Juni 2026
computer-use
Zeit aktualisiert 29. Juli 2026
webapp-testing
Zeit aktualisiert 29. Juni 2026
lark-base
Zeit aktualisiert 5. Juli 2026
OR